162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SGI IOC3 multifunction device driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018, 2019 Thomas Bogendoerfer <tbogendoerfer@suse.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on work by: 862306a36Sopenharmony_ci * Stanislaw Skowronek <skylark@unaligned.org> 962306a36Sopenharmony_ci * Joshua Kinard <kumba@gentoo.org> 1062306a36Sopenharmony_ci * Brent Casavant <bcasavan@sgi.com> - IOC4 master driver 1162306a36Sopenharmony_ci * Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/irqdomain.h> 1862306a36Sopenharmony_ci#include <linux/mfd/core.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/pci.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/platform_data/sgi-w1.h> 2362306a36Sopenharmony_ci#include <linux/rtc/ds1685.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/pci/bridge.h> 2662306a36Sopenharmony_ci#include <asm/sn/ioc3.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define IOC3_IRQ_SERIAL_A 6 2962306a36Sopenharmony_ci#define IOC3_IRQ_SERIAL_B 15 3062306a36Sopenharmony_ci#define IOC3_IRQ_KBD 22 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Bitmask for selecting which IRQs are level triggered */ 3362306a36Sopenharmony_ci#define IOC3_LVL_MASK (BIT(IOC3_IRQ_SERIAL_A) | BIT(IOC3_IRQ_SERIAL_B)) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define M48T35_REG_SIZE 32768 /* size of m48t35 registers */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 1.2 us latency timer (40 cycles at 33 MHz) */ 3862306a36Sopenharmony_ci#define IOC3_LATENCY 40 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct ioc3_priv_data { 4162306a36Sopenharmony_ci struct irq_domain *domain; 4262306a36Sopenharmony_ci struct ioc3 __iomem *regs; 4362306a36Sopenharmony_ci struct pci_dev *pdev; 4462306a36Sopenharmony_ci int domain_irq; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void ioc3_irq_ack(struct irq_data *d) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d); 5062306a36Sopenharmony_ci unsigned int hwirq = irqd_to_hwirq(d); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci writel(BIT(hwirq), &ipd->regs->sio_ir); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void ioc3_irq_mask(struct irq_data *d) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d); 5862306a36Sopenharmony_ci unsigned int hwirq = irqd_to_hwirq(d); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci writel(BIT(hwirq), &ipd->regs->sio_iec); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void ioc3_irq_unmask(struct irq_data *d) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d); 6662306a36Sopenharmony_ci unsigned int hwirq = irqd_to_hwirq(d); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci writel(BIT(hwirq), &ipd->regs->sio_ies); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic struct irq_chip ioc3_irq_chip = { 7262306a36Sopenharmony_ci .name = "IOC3", 7362306a36Sopenharmony_ci .irq_ack = ioc3_irq_ack, 7462306a36Sopenharmony_ci .irq_mask = ioc3_irq_mask, 7562306a36Sopenharmony_ci .irq_unmask = ioc3_irq_unmask, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int ioc3_irq_domain_map(struct irq_domain *d, unsigned int irq, 7962306a36Sopenharmony_ci irq_hw_number_t hwirq) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci /* Set level IRQs for every interrupt contained in IOC3_LVL_MASK */ 8262306a36Sopenharmony_ci if (BIT(hwirq) & IOC3_LVL_MASK) 8362306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_level_irq); 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_edge_irq); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci irq_set_chip_data(irq, d->host_data); 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void ioc3_irq_domain_unmap(struct irq_domain *d, unsigned int irq) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci irq_set_chip_and_handler(irq, NULL, NULL); 9462306a36Sopenharmony_ci irq_set_chip_data(irq, NULL); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic const struct irq_domain_ops ioc3_irq_domain_ops = { 9862306a36Sopenharmony_ci .map = ioc3_irq_domain_map, 9962306a36Sopenharmony_ci .unmap = ioc3_irq_domain_unmap, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void ioc3_irq_handler(struct irq_desc *desc) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct irq_domain *domain = irq_desc_get_handler_data(desc); 10562306a36Sopenharmony_ci struct ioc3_priv_data *ipd = domain->host_data; 10662306a36Sopenharmony_ci struct ioc3 __iomem *regs = ipd->regs; 10762306a36Sopenharmony_ci u32 pending, mask; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pending = readl(®s->sio_ir); 11062306a36Sopenharmony_ci mask = readl(®s->sio_ies); 11162306a36Sopenharmony_ci pending &= mask; /* Mask off not enabled interrupts */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (pending) 11462306a36Sopenharmony_ci generic_handle_domain_irq(domain, __ffs(pending)); 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci spurious_interrupt(); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * System boards/BaseIOs use more interrupt pins of the bridge ASIC 12162306a36Sopenharmony_ci * to which the IOC3 is connected. Since the IOC3 MFD driver 12262306a36Sopenharmony_ci * knows wiring of these extra pins, we use the map_irq function 12362306a36Sopenharmony_ci * to get interrupts activated 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistatic int ioc3_map_irq(struct pci_dev *pdev, int slot, int pin) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct pci_host_bridge *hbrg = pci_find_host_bridge(pdev->bus); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return hbrg->map_irq(pdev, slot, pin); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int ioc3_irq_domain_setup(struct ioc3_priv_data *ipd, int irq) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct irq_domain *domain; 13562306a36Sopenharmony_ci struct fwnode_handle *fn; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci fn = irq_domain_alloc_named_fwnode("IOC3"); 13862306a36Sopenharmony_ci if (!fn) 13962306a36Sopenharmony_ci goto err; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci domain = irq_domain_create_linear(fn, 24, &ioc3_irq_domain_ops, ipd); 14262306a36Sopenharmony_ci if (!domain) { 14362306a36Sopenharmony_ci irq_domain_free_fwnode(fn); 14462306a36Sopenharmony_ci goto err; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ipd->domain = domain; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci irq_set_chained_handler_and_data(irq, ioc3_irq_handler, domain); 15062306a36Sopenharmony_ci ipd->domain_irq = irq; 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cierr: 15462306a36Sopenharmony_ci dev_err(&ipd->pdev->dev, "irq domain setup failed\n"); 15562306a36Sopenharmony_ci return -ENOMEM; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct resource ioc3_uarta_resources[] = { 15962306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uarta), 16062306a36Sopenharmony_ci sizeof_field(struct ioc3, sregs.uarta)), 16162306a36Sopenharmony_ci DEFINE_RES_IRQ(IOC3_IRQ_SERIAL_A) 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic const struct resource ioc3_uartb_resources[] = { 16562306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uartb), 16662306a36Sopenharmony_ci sizeof_field(struct ioc3, sregs.uartb)), 16762306a36Sopenharmony_ci DEFINE_RES_IRQ(IOC3_IRQ_SERIAL_B) 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic struct mfd_cell ioc3_serial_cells[] = { 17162306a36Sopenharmony_ci { 17262306a36Sopenharmony_ci .name = "ioc3-serial8250", 17362306a36Sopenharmony_ci .resources = ioc3_uarta_resources, 17462306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_uarta_resources), 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci { 17762306a36Sopenharmony_ci .name = "ioc3-serial8250", 17862306a36Sopenharmony_ci .resources = ioc3_uartb_resources, 17962306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_uartb_resources), 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int ioc3_serial_setup(struct ioc3_priv_data *ipd) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int ret; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Set gpio pins for RS232/RS422 mode selection */ 18862306a36Sopenharmony_ci writel(GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL, 18962306a36Sopenharmony_ci &ipd->regs->gpcr_s); 19062306a36Sopenharmony_ci /* Select RS232 mode for uart a */ 19162306a36Sopenharmony_ci writel(0, &ipd->regs->gppr[6]); 19262306a36Sopenharmony_ci /* Select RS232 mode for uart b */ 19362306a36Sopenharmony_ci writel(0, &ipd->regs->gppr[7]); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Switch both ports to 16650 mode */ 19662306a36Sopenharmony_ci writel(readl(&ipd->regs->port_a.sscr) & ~SSCR_DMA_EN, 19762306a36Sopenharmony_ci &ipd->regs->port_a.sscr); 19862306a36Sopenharmony_ci writel(readl(&ipd->regs->port_b.sscr) & ~SSCR_DMA_EN, 19962306a36Sopenharmony_ci &ipd->regs->port_b.sscr); 20062306a36Sopenharmony_ci udelay(1000); /* Wait until mode switch is done */ 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 20362306a36Sopenharmony_ci ioc3_serial_cells, ARRAY_SIZE(ioc3_serial_cells), 20462306a36Sopenharmony_ci &ipd->pdev->resource[0], 0, ipd->domain); 20562306a36Sopenharmony_ci if (ret) { 20662306a36Sopenharmony_ci dev_err(&ipd->pdev->dev, "Failed to add 16550 subdevs\n"); 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic const struct resource ioc3_kbd_resources[] = { 21462306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, serio), 21562306a36Sopenharmony_ci sizeof_field(struct ioc3, serio)), 21662306a36Sopenharmony_ci DEFINE_RES_IRQ(IOC3_IRQ_KBD) 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct mfd_cell ioc3_kbd_cells[] = { 22062306a36Sopenharmony_ci { 22162306a36Sopenharmony_ci .name = "ioc3-kbd", 22262306a36Sopenharmony_ci .resources = ioc3_kbd_resources, 22362306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_kbd_resources), 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int ioc3_kbd_setup(struct ioc3_priv_data *ipd) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int ret; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 23262306a36Sopenharmony_ci ioc3_kbd_cells, ARRAY_SIZE(ioc3_kbd_cells), 23362306a36Sopenharmony_ci &ipd->pdev->resource[0], 0, ipd->domain); 23462306a36Sopenharmony_ci if (ret) { 23562306a36Sopenharmony_ci dev_err(&ipd->pdev->dev, "Failed to add 16550 subdevs\n"); 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic const struct resource ioc3_eth_resources[] = { 24362306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, eth), 24462306a36Sopenharmony_ci sizeof_field(struct ioc3, eth)), 24562306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, ssram), 24662306a36Sopenharmony_ci sizeof_field(struct ioc3, ssram)), 24762306a36Sopenharmony_ci DEFINE_RES_IRQ(0) 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic const struct resource ioc3_w1_resources[] = { 25162306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, mcr), 25262306a36Sopenharmony_ci sizeof_field(struct ioc3, mcr)), 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_cistatic struct sgi_w1_platform_data ioc3_w1_platform_data; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic struct mfd_cell ioc3_eth_cells[] = { 25762306a36Sopenharmony_ci { 25862306a36Sopenharmony_ci .name = "ioc3-eth", 25962306a36Sopenharmony_ci .resources = ioc3_eth_resources, 26062306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_eth_resources), 26162306a36Sopenharmony_ci }, 26262306a36Sopenharmony_ci { 26362306a36Sopenharmony_ci .name = "sgi_w1", 26462306a36Sopenharmony_ci .resources = ioc3_w1_resources, 26562306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_w1_resources), 26662306a36Sopenharmony_ci .platform_data = &ioc3_w1_platform_data, 26762306a36Sopenharmony_ci .pdata_size = sizeof(ioc3_w1_platform_data), 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int ioc3_eth_setup(struct ioc3_priv_data *ipd) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci int ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Enable One-Wire bus */ 27662306a36Sopenharmony_ci writel(GPCR_MLAN_EN, &ipd->regs->gpcr_s); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Generate unique identifier */ 27962306a36Sopenharmony_ci snprintf(ioc3_w1_platform_data.dev_id, 28062306a36Sopenharmony_ci sizeof(ioc3_w1_platform_data.dev_id), "ioc3-%012llx", 28162306a36Sopenharmony_ci ipd->pdev->resource->start); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 28462306a36Sopenharmony_ci ioc3_eth_cells, ARRAY_SIZE(ioc3_eth_cells), 28562306a36Sopenharmony_ci &ipd->pdev->resource[0], ipd->pdev->irq, NULL); 28662306a36Sopenharmony_ci if (ret) { 28762306a36Sopenharmony_ci dev_err(&ipd->pdev->dev, "Failed to add ETH/W1 subdev\n"); 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic const struct resource ioc3_m48t35_resources[] = { 29562306a36Sopenharmony_ci DEFINE_RES_MEM(IOC3_BYTEBUS_DEV0, M48T35_REG_SIZE) 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct mfd_cell ioc3_m48t35_cells[] = { 29962306a36Sopenharmony_ci { 30062306a36Sopenharmony_ci .name = "rtc-m48t35", 30162306a36Sopenharmony_ci .resources = ioc3_m48t35_resources, 30262306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_m48t35_resources), 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int ioc3_m48t35_setup(struct ioc3_priv_data *ipd) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci int ret; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 31162306a36Sopenharmony_ci ioc3_m48t35_cells, ARRAY_SIZE(ioc3_m48t35_cells), 31262306a36Sopenharmony_ci &ipd->pdev->resource[0], 0, ipd->domain); 31362306a36Sopenharmony_ci if (ret) 31462306a36Sopenharmony_ci dev_err(&ipd->pdev->dev, "Failed to add M48T35 subdev\n"); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return ret; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic struct ds1685_rtc_platform_data ip30_rtc_platform_data = { 32062306a36Sopenharmony_ci .bcd_mode = false, 32162306a36Sopenharmony_ci .no_irq = false, 32262306a36Sopenharmony_ci .uie_unsupported = true, 32362306a36Sopenharmony_ci .access_type = ds1685_reg_indirect, 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic const struct resource ioc3_rtc_ds1685_resources[] = { 32762306a36Sopenharmony_ci DEFINE_RES_MEM(IOC3_BYTEBUS_DEV1, 1), 32862306a36Sopenharmony_ci DEFINE_RES_MEM(IOC3_BYTEBUS_DEV2, 1), 32962306a36Sopenharmony_ci DEFINE_RES_IRQ(0) 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic struct mfd_cell ioc3_ds1685_cells[] = { 33362306a36Sopenharmony_ci { 33462306a36Sopenharmony_ci .name = "rtc-ds1685", 33562306a36Sopenharmony_ci .resources = ioc3_rtc_ds1685_resources, 33662306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_rtc_ds1685_resources), 33762306a36Sopenharmony_ci .platform_data = &ip30_rtc_platform_data, 33862306a36Sopenharmony_ci .pdata_size = sizeof(ip30_rtc_platform_data), 33962306a36Sopenharmony_ci .id = PLATFORM_DEVID_NONE, 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci}; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int ioc3_ds1685_setup(struct ioc3_priv_data *ipd) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci int ret, irq; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci irq = ioc3_map_irq(ipd->pdev, 6, 0); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = mfd_add_devices(&ipd->pdev->dev, 0, ioc3_ds1685_cells, 35062306a36Sopenharmony_ci ARRAY_SIZE(ioc3_ds1685_cells), 35162306a36Sopenharmony_ci &ipd->pdev->resource[0], irq, NULL); 35262306a36Sopenharmony_ci if (ret) 35362306a36Sopenharmony_ci dev_err(&ipd->pdev->dev, "Failed to add DS1685 subdev\n"); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci}; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic const struct resource ioc3_leds_resources[] = { 36062306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, gppr[0]), 36162306a36Sopenharmony_ci sizeof_field(struct ioc3, gppr[0])), 36262306a36Sopenharmony_ci DEFINE_RES_MEM(offsetof(struct ioc3, gppr[1]), 36362306a36Sopenharmony_ci sizeof_field(struct ioc3, gppr[1])), 36462306a36Sopenharmony_ci}; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic struct mfd_cell ioc3_led_cells[] = { 36762306a36Sopenharmony_ci { 36862306a36Sopenharmony_ci .name = "ip30-leds", 36962306a36Sopenharmony_ci .resources = ioc3_leds_resources, 37062306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(ioc3_leds_resources), 37162306a36Sopenharmony_ci .id = PLATFORM_DEVID_NONE, 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci}; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int ioc3_led_setup(struct ioc3_priv_data *ipd) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci int ret; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret = mfd_add_devices(&ipd->pdev->dev, 0, ioc3_led_cells, 38062306a36Sopenharmony_ci ARRAY_SIZE(ioc3_led_cells), 38162306a36Sopenharmony_ci &ipd->pdev->resource[0], 0, ipd->domain); 38262306a36Sopenharmony_ci if (ret) 38362306a36Sopenharmony_ci dev_err(&ipd->pdev->dev, "Failed to add LED subdev\n"); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return ret; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int ip27_baseio_setup(struct ioc3_priv_data *ipd) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int ret, io_irq; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 39362306a36Sopenharmony_ci PCI_INTERRUPT_INTB); 39462306a36Sopenharmony_ci ret = ioc3_irq_domain_setup(ipd, io_irq); 39562306a36Sopenharmony_ci if (ret) 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ret = ioc3_eth_setup(ipd); 39962306a36Sopenharmony_ci if (ret) 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ret = ioc3_serial_setup(ipd); 40362306a36Sopenharmony_ci if (ret) 40462306a36Sopenharmony_ci return ret; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return ioc3_m48t35_setup(ipd); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int ip27_baseio6g_setup(struct ioc3_priv_data *ipd) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci int ret, io_irq; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 41462306a36Sopenharmony_ci PCI_INTERRUPT_INTB); 41562306a36Sopenharmony_ci ret = ioc3_irq_domain_setup(ipd, io_irq); 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci return ret; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret = ioc3_eth_setup(ipd); 42062306a36Sopenharmony_ci if (ret) 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = ioc3_serial_setup(ipd); 42462306a36Sopenharmony_ci if (ret) 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ret = ioc3_m48t35_setup(ipd); 42862306a36Sopenharmony_ci if (ret) 42962306a36Sopenharmony_ci return ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return ioc3_kbd_setup(ipd); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int ip27_mio_setup(struct ioc3_priv_data *ipd) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci ret = ioc3_irq_domain_setup(ipd, ipd->pdev->irq); 43962306a36Sopenharmony_ci if (ret) 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ret = ioc3_serial_setup(ipd); 44362306a36Sopenharmony_ci if (ret) 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return ioc3_kbd_setup(ipd); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int ip30_sysboard_setup(struct ioc3_priv_data *ipd) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci int ret, io_irq; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 45462306a36Sopenharmony_ci PCI_INTERRUPT_INTB); 45562306a36Sopenharmony_ci ret = ioc3_irq_domain_setup(ipd, io_irq); 45662306a36Sopenharmony_ci if (ret) 45762306a36Sopenharmony_ci return ret; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = ioc3_eth_setup(ipd); 46062306a36Sopenharmony_ci if (ret) 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ret = ioc3_serial_setup(ipd); 46462306a36Sopenharmony_ci if (ret) 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = ioc3_kbd_setup(ipd); 46862306a36Sopenharmony_ci if (ret) 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = ioc3_ds1685_setup(ipd); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return ioc3_led_setup(ipd); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int ioc3_menet_setup(struct ioc3_priv_data *ipd) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci int ret, io_irq; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 48362306a36Sopenharmony_ci PCI_INTERRUPT_INTB); 48462306a36Sopenharmony_ci ret = ioc3_irq_domain_setup(ipd, io_irq); 48562306a36Sopenharmony_ci if (ret) 48662306a36Sopenharmony_ci return ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ret = ioc3_eth_setup(ipd); 48962306a36Sopenharmony_ci if (ret) 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return ioc3_serial_setup(ipd); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int ioc3_menet4_setup(struct ioc3_priv_data *ipd) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci return ioc3_eth_setup(ipd); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int ioc3_cad_duo_setup(struct ioc3_priv_data *ipd) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci int ret, io_irq; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 50562306a36Sopenharmony_ci PCI_INTERRUPT_INTB); 50662306a36Sopenharmony_ci ret = ioc3_irq_domain_setup(ipd, io_irq); 50762306a36Sopenharmony_ci if (ret) 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = ioc3_eth_setup(ipd); 51162306a36Sopenharmony_ci if (ret) 51262306a36Sopenharmony_ci return ret; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return ioc3_kbd_setup(ipd); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* Helper macro for filling ioc3_info array */ 51862306a36Sopenharmony_ci#define IOC3_SID(_name, _sid, _setup) \ 51962306a36Sopenharmony_ci { \ 52062306a36Sopenharmony_ci .name = _name, \ 52162306a36Sopenharmony_ci .sid = PCI_VENDOR_ID_SGI | (IOC3_SUBSYS_ ## _sid << 16), \ 52262306a36Sopenharmony_ci .setup = _setup, \ 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic struct { 52662306a36Sopenharmony_ci const char *name; 52762306a36Sopenharmony_ci u32 sid; 52862306a36Sopenharmony_ci int (*setup)(struct ioc3_priv_data *ipd); 52962306a36Sopenharmony_ci} ioc3_infos[] = { 53062306a36Sopenharmony_ci IOC3_SID("IP27 BaseIO6G", IP27_BASEIO6G, &ip27_baseio6g_setup), 53162306a36Sopenharmony_ci IOC3_SID("IP27 MIO", IP27_MIO, &ip27_mio_setup), 53262306a36Sopenharmony_ci IOC3_SID("IP27 BaseIO", IP27_BASEIO, &ip27_baseio_setup), 53362306a36Sopenharmony_ci IOC3_SID("IP29 System Board", IP29_SYSBOARD, &ip27_baseio6g_setup), 53462306a36Sopenharmony_ci IOC3_SID("IP30 System Board", IP30_SYSBOARD, &ip30_sysboard_setup), 53562306a36Sopenharmony_ci IOC3_SID("MENET", MENET, &ioc3_menet_setup), 53662306a36Sopenharmony_ci IOC3_SID("MENET4", MENET4, &ioc3_menet4_setup) 53762306a36Sopenharmony_ci}; 53862306a36Sopenharmony_ci#undef IOC3_SID 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic int ioc3_setup(struct ioc3_priv_data *ipd) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci u32 sid; 54362306a36Sopenharmony_ci int i; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* Clear IRQs */ 54662306a36Sopenharmony_ci writel(~0, &ipd->regs->sio_iec); 54762306a36Sopenharmony_ci writel(~0, &ipd->regs->sio_ir); 54862306a36Sopenharmony_ci writel(0, &ipd->regs->eth.eier); 54962306a36Sopenharmony_ci writel(~0, &ipd->regs->eth.eisr); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Read subsystem vendor id and subsystem id */ 55262306a36Sopenharmony_ci pci_read_config_dword(ipd->pdev, PCI_SUBSYSTEM_VENDOR_ID, &sid); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ioc3_infos); i++) 55562306a36Sopenharmony_ci if (sid == ioc3_infos[i].sid) { 55662306a36Sopenharmony_ci pr_info("ioc3: %s\n", ioc3_infos[i].name); 55762306a36Sopenharmony_ci return ioc3_infos[i].setup(ipd); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Treat everything not identified by PCI subid as CAD DUO */ 56162306a36Sopenharmony_ci pr_info("ioc3: CAD DUO\n"); 56262306a36Sopenharmony_ci return ioc3_cad_duo_setup(ipd); 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int ioc3_mfd_probe(struct pci_dev *pdev, 56662306a36Sopenharmony_ci const struct pci_device_id *pci_id) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct ioc3_priv_data *ipd; 56962306a36Sopenharmony_ci struct ioc3 __iomem *regs; 57062306a36Sopenharmony_ci int ret; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = pci_enable_device(pdev); 57362306a36Sopenharmony_ci if (ret) 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_LATENCY_TIMER, IOC3_LATENCY); 57762306a36Sopenharmony_ci pci_set_master(pdev); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 58062306a36Sopenharmony_ci if (ret) { 58162306a36Sopenharmony_ci pr_err("%s: No usable DMA configuration, aborting.\n", 58262306a36Sopenharmony_ci pci_name(pdev)); 58362306a36Sopenharmony_ci goto out_disable_device; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Set up per-IOC3 data */ 58762306a36Sopenharmony_ci ipd = devm_kzalloc(&pdev->dev, sizeof(struct ioc3_priv_data), 58862306a36Sopenharmony_ci GFP_KERNEL); 58962306a36Sopenharmony_ci if (!ipd) { 59062306a36Sopenharmony_ci ret = -ENOMEM; 59162306a36Sopenharmony_ci goto out_disable_device; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci ipd->pdev = pdev; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * Map all IOC3 registers. These are shared between subdevices 59762306a36Sopenharmony_ci * so the main IOC3 module manages them. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci regs = pci_ioremap_bar(pdev, 0); 60062306a36Sopenharmony_ci if (!regs) { 60162306a36Sopenharmony_ci dev_warn(&pdev->dev, "ioc3: Unable to remap PCI BAR for %s.\n", 60262306a36Sopenharmony_ci pci_name(pdev)); 60362306a36Sopenharmony_ci ret = -ENOMEM; 60462306a36Sopenharmony_ci goto out_disable_device; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci ipd->regs = regs; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Track PCI-device specific data */ 60962306a36Sopenharmony_ci pci_set_drvdata(pdev, ipd); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = ioc3_setup(ipd); 61262306a36Sopenharmony_ci if (ret) { 61362306a36Sopenharmony_ci /* Remove all already added MFD devices */ 61462306a36Sopenharmony_ci mfd_remove_devices(&ipd->pdev->dev); 61562306a36Sopenharmony_ci if (ipd->domain) { 61662306a36Sopenharmony_ci struct fwnode_handle *fn = ipd->domain->fwnode; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci irq_domain_remove(ipd->domain); 61962306a36Sopenharmony_ci irq_domain_free_fwnode(fn); 62062306a36Sopenharmony_ci free_irq(ipd->domain_irq, (void *)ipd); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci pci_iounmap(pdev, regs); 62362306a36Sopenharmony_ci goto out_disable_device; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return 0; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ciout_disable_device: 62962306a36Sopenharmony_ci pci_disable_device(pdev); 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void ioc3_mfd_remove(struct pci_dev *pdev) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct ioc3_priv_data *ipd; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ipd = pci_get_drvdata(pdev); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* Clear and disable all IRQs */ 64062306a36Sopenharmony_ci writel(~0, &ipd->regs->sio_iec); 64162306a36Sopenharmony_ci writel(~0, &ipd->regs->sio_ir); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Release resources */ 64462306a36Sopenharmony_ci mfd_remove_devices(&ipd->pdev->dev); 64562306a36Sopenharmony_ci if (ipd->domain) { 64662306a36Sopenharmony_ci struct fwnode_handle *fn = ipd->domain->fwnode; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci irq_domain_remove(ipd->domain); 64962306a36Sopenharmony_ci irq_domain_free_fwnode(fn); 65062306a36Sopenharmony_ci free_irq(ipd->domain_irq, (void *)ipd); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci pci_iounmap(pdev, ipd->regs); 65362306a36Sopenharmony_ci pci_disable_device(pdev); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic struct pci_device_id ioc3_mfd_id_table[] = { 65762306a36Sopenharmony_ci { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID }, 65862306a36Sopenharmony_ci { 0, }, 65962306a36Sopenharmony_ci}; 66062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ioc3_mfd_id_table); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic struct pci_driver ioc3_mfd_driver = { 66362306a36Sopenharmony_ci .name = "IOC3", 66462306a36Sopenharmony_ci .id_table = ioc3_mfd_id_table, 66562306a36Sopenharmony_ci .probe = ioc3_mfd_probe, 66662306a36Sopenharmony_ci .remove = ioc3_mfd_remove, 66762306a36Sopenharmony_ci}; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cimodule_pci_driver(ioc3_mfd_driver); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ciMODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfer@suse.de>"); 67262306a36Sopenharmony_ciMODULE_DESCRIPTION("SGI IOC3 MFD driver"); 67362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 674