Thread (11 messages) 11 messages, 4 authors, 2019-05-14

Re: [PATCH v2 2/4] mfd: ioc3: Add driver for SGI IOC3 chip

From: Lee Jones <hidden>
Date: 2019-05-08 10:23:21
Also in: linux-input, linux-mips, linux-rtc, linux-serial, lkml

On Tue, 09 Apr 2019, Thomas Bogendoerfer wrote:
SGI IOC3 chip has integrated ethernet, keyboard and mouse interface.
It also supports connecting a SuperIO chip for serial and parallel
interfaces. IOC3 is used inside various SGI systemboards and add-on
cards with different equipped external interfaces.

Support for ethernet and serial interfaces were implemented inside
the network driver. This patchset moves out the not network related
parts to a new MFD driver, which takes care of card detection,
setup of platform devices and interrupt distribution for the subdevices.

Signed-off-by: Thomas Bogendoerfer <redacted>
---
 arch/mips/include/asm/sn/ioc3.h       |  345 +++---
 arch/mips/sgi-ip27/ip27-timer.c       |   20 -
 drivers/mfd/Kconfig                   |   13 +
 drivers/mfd/Makefile                  |    1 +
 drivers/mfd/ioc3.c                    |  802 ++++++++++++++
 drivers/net/ethernet/sgi/Kconfig      |    4 +-
 drivers/net/ethernet/sgi/ioc3-eth.c   | 1867 ++++++++++++---------------------
 drivers/tty/serial/8250/8250_ioc3.c   |   98 ++
 drivers/tty/serial/8250/Kconfig       |   11 +
 drivers/tty/serial/8250/Makefile      |    1 +
 include/linux/platform_data/ioc3eth.h |   15 +
 11 files changed, 1762 insertions(+), 1415 deletions(-)
 create mode 100644 drivers/mfd/ioc3.c
 create mode 100644 drivers/tty/serial/8250/8250_ioc3.c
 create mode 100644 include/linux/platform_data/ioc3eth.h
[...]
quoted hunk ↗ jump to hunk
+config SGI_MFD_IOC3
+	tristate "SGI IOC3 core driver"
+	depends on PCI && MIPS
+	select MFD_CORE
+	help
+	  This option enables basic support for the SGI IOC3-based
+	  controller cards.  This option does not enable any specific
+	  functions on such a card, but provides necessary infrastructure
+	  for other drivers to utilize.
+
+	  If you have an SGI Origin, Octane, or a PCI IOC3 card,
+	  then say Y. Otherwise say N.
+
 endmenu
 endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b4569ed7f3f3..07255e499129 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -246,4 +246,5 @@ obj-$(CONFIG_MFD_MXS_LRADC)     += mxs-lradc.o
 obj-$(CONFIG_MFD_SC27XX_PMIC)	+= sprd-sc27xx-spi.o
 obj-$(CONFIG_RAVE_SP_CORE)	+= rave-sp.o
 obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
+obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
 
diff --git a/drivers/mfd/ioc3.c b/drivers/mfd/ioc3.c
new file mode 100644
index 000000000000..ad715805b16e
--- /dev/null
+++ b/drivers/mfd/ioc3.c
@@ -0,0 +1,802 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SGI IOC3 multifunction device driver
+ *
+ * Copyright (C) 2018, 2019 Thomas Bogendoerfer <tbogendoerfer@suse.de>
+ *
+ * Based on work by:
+ *   Stanislaw Skowronek <skylark@unaligned.org>
+ *   Joshua Kinard <kumba@gentoo.org>
+ *   Brent Casavant <bcasavan@sgi.com> - IOC4 master driver
+ *   Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/ioc3eth.h>
These should be in alphabetical order.
+#include <asm/sn/ioc3.h>
+#include <asm/pci/bridge.h>
+
+#define IOC3_ETH	BIT(0)
+#define IOC3_SER	BIT(1)
+#define IOC3_PAR	BIT(2)
+#define IOC3_KBD	BIT(3)
+#define IOC3_M48T35	BIT(4)
+
+static int ioc3_serial_id = 1;
+static int ioc3_eth_id = 1;
+static int ioc3_kbd_id = 1;
+static struct mfd_cell ioc3_mfd_cells[5];
+
+struct ioc3_board_info {
+	const char *name;
+	int irq_offset;
+	int funcs;
+};
+
+struct ioc3_priv_data {
+	struct ioc3_board_info *info;
+	struct irq_domain *domain;
+	struct ioc3 __iomem *regs;
+	struct pci_dev *pdev;
+	char nic_part[32];
+	char nic_mac[6];
+	int irq_io;
+};
+
+#define MCR_PACK(pulse, sample)	(((pulse) << 10) | ((sample) << 2))
+
+static int ioc3_nic_wait(u32 __iomem *mcr)
+{
+	u32 mcr_val;
+
+	do {
+		mcr_val = readl(mcr);
+	} while (!(mcr_val & 2));
Why 2?  Is bit 2 always set on a successful read?

Either way, you should provide a comment to improve readability.
+	return (mcr_val & 1);
Reads just a bit at a time?  Again, a comment please.
+}
+
+static int ioc3_nic_reset(u32 __iomem *mcr)
+{
+	int presence;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	writel(MCR_PACK(520, 65), mcr);
What do these magic values mean?  Best to define them.
+	presence = ioc3_nic_wait(mcr);
+	local_irq_restore(flags);
+
+	udelay(500);
What are you waiting for?  Why 500us?
+	return presence;
+}
+
+static int ioc3_nic_read_bit(u32 __iomem *mcr)
+{
+	int result;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	writel(MCR_PACK(6, 13), mcr);
Why 6 and 13?

Define all magic values please.
+	result = ioc3_nic_wait(mcr);
+	local_irq_restore(flags);
+
+	udelay(100);
Why 100?
+	return result;
+}
+
+static u32 ioc3_nic_read_byte(u32 __iomem *mcr)
+{
+	u32 result = 0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		result = ((result >> 1) | (ioc3_nic_read_bit(mcr) << 7));
+
+	return result;
+}
+
+static void ioc3_nic_write_bit(u32 __iomem *mcr, int bit)
+{
+	if (bit)
+		writel(MCR_PACK(6, 110), mcr);
+	else
+		writel(MCR_PACK(80, 30), mcr);
+
+	ioc3_nic_wait(mcr);
+}
+
+static void ioc3_nic_write_byte(u32 __iomem *mcr, int byte)
+{
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		ioc3_nic_write_bit(mcr, byte & 1);
+		byte >>= 1;
+	}
+}
+
+static u64 ioc3_nic_find(u32 __iomem *mcr, int *last, u64 addr)
+{
+	int a, b, index, disc;
+
+	ioc3_nic_reset(mcr);
+
+	/* Search ROM.  */
+	ioc3_nic_write_byte(mcr, 0xf0);
What does 0xf0 mean?

Please define it and all magic numbers that follow.
+	/* Algorithm from ``Book of iButton Standards''.  */
That's great, but what is it actually doing.  Please provide suitable
comments such that the reader doesn't have to painstakingly work it
all out.
+	for (index = 0, disc = 0; index < 64; index++) {
+		a = ioc3_nic_read_bit(mcr);
+		b = ioc3_nic_read_bit(mcr);
+
+		if (unlikely(a && b)) {
+			pr_warn("ioc3: NIC search failed.\n");
+			*last = 0;
+			return 0;
+		}
+
+		if (!a && !b) {
+			if (index == *last)
+				addr |= 1UL << index;
+			else if (index > *last) {
+				addr &= ~(1UL << index);
+				disc = index;
+			} else if ((addr & (1UL << index)) == 0)
+				disc = index;
+			ioc3_nic_write_bit(mcr, (addr >> index) & 1);
+			continue;
+		} else {
+			if (a)
+				addr |= (1UL << index);
+			else
+				addr &= ~(1UL << index);
+			ioc3_nic_write_bit(mcr, a);
+			continue;
+		}
+	}
+	*last = disc;
+	return addr;
+}
+
+static void ioc3_nic_addr(u32 __iomem *mcr, u64 addr)
+{
+	int index;
+
+	ioc3_nic_reset(mcr);
+	ioc3_nic_write_byte(mcr, 0xf0);
+
+	for (index = 0; index < 64; index++) {
+		ioc3_nic_read_bit(mcr);
+		ioc3_nic_read_bit(mcr);
+		ioc3_nic_write_bit(mcr, ((addr >> index) & 1));
+	}
What is this function doing?  Setting the NIC address?

Why are the 2 sequential reads required before each bit write?
+}
+
+static void crc16_byte(u32 *crc, u8 db)
+{
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		*crc <<= 1;
+		if ((db ^ (*crc >> 16)) & 1)
+			*crc ^= 0x8005;
+		db >>= 1;
+	}
+	*crc &= 0xffff;
+}
+
+static u32 crc16_area(u8 *dbs, int size, u32 crc)
+{
+	while (size--)
+		crc16_byte(&crc, *(dbs++));
+
+	return crc;
+}
+
+static void crc8_byte(u32 *crc, u8 db)
+{
+	int i, f;
+
+	for (i = 0; i < 8; i++) {
+		f = ((*crc ^ db) & 1);
+		*crc >>= 1;
+		db >>= 1;
+		if (f)
+			*crc ^= 0x8c;
+	}
+	*crc &= 0xff;
+}
+
+static u32 crc8_addr(u64 addr)
+{
+	u32 crc = 0;
+	int i;
+
+	for (i = 0; i < 64; i += 8)
+		crc8_byte(&crc, addr >> i);
+	return crc;
+}
Not looked into these in any detail, but are you not able to use the
CRC functions already provided by the kernel?
+static void ioc3_read_redir_page(u32 __iomem *mcr, u64 addr, int page,
What is a redir page?
+				 u8 *redir, u8 *data)
+{
+	int loops = 16, i;
+
+	while (redir[page] != 0xff) {
+		page = (redir[page] ^ 0xff);
+		loops--;
+		if (unlikely(loops < 0)) {
+			pr_err("ioc3: NIC circular redirection\n");
+			return;
+		}
+	}
+
+	loops = 3;
+	while (loops > 0) {
+		ioc3_nic_addr(mcr, addr);
+		ioc3_nic_write_byte(mcr, 0xf0);
+		ioc3_nic_write_byte(mcr, (page << 5) & 0xe0);
+		ioc3_nic_write_byte(mcr, (page >> 3) & 0x1f);
+
+		for (i = 0; i < 0x20; i++)
+			data[i] = ioc3_nic_read_byte(mcr);
+
+		if (crc16_area(data, 0x20, 0) == 0x800d)
+			return;
+
+		loops--;
+	}
+
+	pr_err("ioc3: CRC error in data page\n");
+	for (i = 0; i < 0x20; i++)
+		data[i] = 0x00;
Comments required throughout.
+}
+
+static void ioc3_read_redir_map(u32 __iomem *mcr, u64 addr, u8 *redir)
+{
+	int i, j, crc_ok, loops = 3;
+	u32 crc;
+
+	while (loops > 0) {
+		crc_ok = 1;
+		ioc3_nic_addr(mcr, addr);
+		ioc3_nic_write_byte(mcr, 0xaa);
+		ioc3_nic_write_byte(mcr, 0x00);
+		ioc3_nic_write_byte(mcr, 0x01);
+
+		for (i = 0; i < 64; i += 8) {
+			for (j = 0; j < 8; j++)
+				redir[i + j] = ioc3_nic_read_byte(mcr);
+
+			crc = crc16_area(redir + i, 8, i == 0 ? 0x8707 : 0);
+
+			crc16_byte(&crc, ioc3_nic_read_byte(mcr));
+			crc16_byte(&crc, ioc3_nic_read_byte(mcr));
+
+			if (crc != 0x800d)
+				crc_ok = 0;
+		}
+		if (crc_ok)
+			return;
+		loops--;
+	}
+	pr_err("ioc3: CRC error in redirection page\n");
+	for (i = 0; i < 64; i++)
+		redir[i] = 0xff;
As above.
+}
+
+static void ioc3_read_nic(struct ioc3_priv_data *ipd, u32 __iomem *mcr,
+			  u64 addr)
+{
+	u8 redir[64];
+	u8 data[64], part[32];
+	int i, j;
+
+	/* Read redirections */
+	ioc3_read_redir_map(mcr, addr, redir);
+
+	/* Read data pages */
+	ioc3_read_redir_page(mcr, addr, 0, redir, data);
+	ioc3_read_redir_page(mcr, addr, 1, redir, (data + 32));
+
+	/* Assemble the part # */
+	j = 0;
+	for (i = 0; i < 19; i++)
+		if (data[i + 11] != ' ')
+			part[j++] = data[i + 11];
+
+	for (i = 0; i < 6; i++)
+		if (data[i + 32] != ' ')
+			part[j++] = data[i + 32];
+
+	part[j] = 0;
+
+	/* Skip Octane (IP30) power supplies */
+	if (!(strncmp(part, "060-0035-", 9)) ||
+	    !(strncmp(part, "060-0038-", 9)) ||
+	    !(strncmp(part, "060-0028-", 9)))
+		return;
+
+	strlcpy(ipd->nic_part, part, sizeof(ipd->nic_part));
+}
+
+static void ioc3_read_mac(struct ioc3_priv_data *ipd, u64 addr)
+{
+	int i, loops = 3;
+	u8 data[13];
+	u32 __iomem *mcr = &ipd->regs->mcr;
+
+	while (loops > 0) {
+		ioc3_nic_addr(mcr, addr);
+		ioc3_nic_write_byte(mcr, 0xf0);
+		ioc3_nic_write_byte(mcr, 0x00);
+		ioc3_nic_write_byte(mcr, 0x00);
+		ioc3_nic_read_byte(mcr);
+
+		for (i = 0; i < 13; i++)
+			data[i] = ioc3_nic_read_byte(mcr);
+
+		if (crc16_area(data, 13, 0) == 0x800d) {
+			for (i = 10; i > 4; i--)
+				ipd->nic_mac[10 - i] = data[i];
+			return;
+		}
+		loops--;
+	}
+
+	pr_err("ioc3: CRC error in MAC address\n");
+	for (i = 0; i < 6; i++)
+		ipd->nic_mac[i] = 0x00;
+}
+
+static void ioc3_probe_nic(struct ioc3_priv_data *ipd, u32 __iomem *mcr)
+{
+	int save = 0, loops = 3;
+	u64 first, addr;
+
+	while (loops > 0) {
+		ipd->nic_part[0] = 0;
+		first = ioc3_nic_find(mcr, &save, 0);
+		addr = first;
+
+		if (unlikely(!first))
+			return;
+
+		while (1) {
+			if (crc8_addr(addr))
+				break;
+
+			switch (addr & 0xff) {
+			case 0x0b:
+				ioc3_read_nic(ipd, mcr, addr);
+				break;
+			case 0x09:
+			case 0x89:
+			case 0x91:
+				ioc3_read_mac(ipd, addr);
+				break;
+			}
+
+			addr = ioc3_nic_find(mcr, &save, addr);
+			if (addr == first)
+				return;
+		}
+		loops--;
+	}
+	pr_err("ioc3: CRC error in NIC address\n");
+}
This all looks like networking code.  If this is the case, it should
be moved to drivers/networking or similar.
+static void ioc3_irq_ack(struct irq_data *d)
+{
+	struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d);
+	unsigned int hwirq = irqd_to_hwirq(d);
+
+	writel(BIT(hwirq), &ipd->regs->sio_ir);
+}
+
+static void ioc3_irq_mask(struct irq_data *d)
+{
+	struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d);
+	unsigned int hwirq = irqd_to_hwirq(d);
+
+	writel(BIT(hwirq), &ipd->regs->sio_iec);
+}
+
+static void ioc3_irq_unmask(struct irq_data *d)
+{
+	struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d);
+	unsigned int hwirq = irqd_to_hwirq(d);
+
+	writel(BIT(hwirq), &ipd->regs->sio_ies);
+}
+
+static struct irq_chip ioc3_irq_chip = {
+	.name		= "IOC3",
+	.irq_ack	= ioc3_irq_ack,
+	.irq_mask	= ioc3_irq_mask,
+	.irq_unmask	= ioc3_irq_unmask,
+};
+
+#define IOC3_LVL_MASK	(BIT(0) | BIT(2) | BIT(6) | BIT(9) | BIT(11) | BIT(15))
You need to define what each of these bits are.  BIT(2) doesn't tell
the reader anything, meaning the code is unreadable.
+static int ioc3_irq_domain_map(struct irq_domain *d, unsigned int irq,
+			      irq_hw_number_t hwirq)
+{
+	if (BIT(hwirq) & IOC3_LVL_MASK)
Comment needed.
+		irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_level_irq);
+	else
+		irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_edge_irq);
+
+	irq_set_chip_data(irq, d->host_data);
+	return 0;
+}
+
+
Drop the superfluous '\n'.
+static const struct irq_domain_ops ioc3_irq_domain_ops = {
+	.map = ioc3_irq_domain_map,
+};
+
+static void ioc3_irq_handler(struct irq_desc *desc)
+{
+	struct irq_domain *domain = irq_desc_get_handler_data(desc);
+	struct ioc3_priv_data *ipd = domain->host_data;
+	struct ioc3 __iomem *regs = ipd->regs;
+	unsigned int irq = 0;
Why does these need to be pre-assigned?
+	u32 pending;
+
+	pending = readl(&regs->sio_ir);
+	pending &= readl(&regs->sio_ies);
Comment required.
+	if (pending)
+		irq = irq_find_mapping(domain, __ffs(pending));
+	else if (!ipd->info->irq_offset &&
+		 (readl(&regs->eth.eisr) & readl(&regs->eth.eier)))
Comment required.
+		irq = irq_find_mapping(domain, 23);
+
+	if (irq)
+		generic_handle_irq(irq);
+	else
+		spurious_interrupt();
+}
+
+static struct resource ioc3_uarta_resources[] = {
+	DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uarta),
You are the first user of offsetof() in MFD.  Could you tell me why
it's required please?
+		       sizeof_field(struct ioc3, sregs.uarta)),
+	DEFINE_RES_IRQ(6)
Please define all magic numbers.
+};
+
+static struct resource ioc3_uartb_resources[] = {
+	DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uartb),
+		       sizeof_field(struct ioc3, sregs.uartb)),
+	DEFINE_RES_IRQ(15)
+};
+
+static struct resource ioc3_kbd_resources[] = {
+	DEFINE_RES_MEM(offsetof(struct ioc3, serio),
+		       sizeof_field(struct ioc3, serio)),
+	DEFINE_RES_IRQ(22)
+};
+
+static struct resource ioc3_eth_resources[] = {
+	DEFINE_RES_MEM(offsetof(struct ioc3, eth),
+		       sizeof_field(struct ioc3, eth)),
+	DEFINE_RES_MEM(offsetof(struct ioc3, ssram),
+		       sizeof_field(struct ioc3, ssram)),
+	DEFINE_RES_IRQ(0)
+};
+
+static struct ioc3eth_platform_data ioc3_eth_platform_data;
I doubt you'll need this in here.  The MAC address info will need to
be moved out to a networking driver of some sort.
+#ifdef CONFIG_SGI_IP27
#ifery in C files is highly discouraged.  Please remove them.
+static struct resource ioc3_rtc_resources[] = {
+	DEFINE_RES_MEM(IOC3_BYTEBUS_DEV0, 32768)
Define all magic numbers please.
+};
+
+static struct ioc3_board_info ip27_baseio_info = {
+	.name = "IP27 BaseIO",
+	.funcs = IOC3_ETH | IOC3_SER | IOC3_M48T35,
+	.irq_offset = 2
+};
+
+static struct ioc3_board_info ip27_baseio6g_info = {
+	.name = "IP27 BaseIO6G",
+	.funcs = IOC3_ETH | IOC3_SER | IOC3_KBD | IOC3_M48T35,
+	.irq_offset = 2
+};
+
+static struct ioc3_board_info ip27_mio_info = {
+	.name = "MIO",
+	.funcs = IOC3_SER | IOC3_PAR | IOC3_KBD,
+	.irq_offset = 0
+};
+
+static struct ioc3_board_info ip29_baseio_info = {
+	.name = "IP29 System Board",
+	.funcs = IOC3_ETH | IOC3_SER | IOC3_PAR | IOC3_KBD | IOC3_M48T35,
+	.irq_offset = 1
+};
+
+#endif /* CONFIG_SGI_IP27 */
+
+static struct ioc3_board_info ioc3_menet_info = {
+	.name = "MENET",
+	.funcs = IOC3_ETH | IOC3_SER,
+	.irq_offset = 4
+};
+
+static struct ioc3_board_info ioc3_cad_duo_info = {
+	.name = "CAD DUO",
+	.funcs = IOC3_ETH | IOC3_KBD,
+	.irq_offset = 0
+};
Please drop all of these and statically create the MFD cells like
almost all other MFD drivers do.
+#define IOC3_BOARD(_partno, _info) {   .info = _info, .match = _partno }
+
+static struct {
+	struct ioc3_board_info *info;
+	const char *match;
+} ioc3_boards[] = {
+#ifdef CONFIG_SGI_IP27
+	IOC3_BOARD("030-0734-", &ip27_baseio6g_info),
+	IOC3_BOARD("030-1023-", &ip27_baseio_info),
+	IOC3_BOARD("030-1124-", &ip27_baseio_info),
+	IOC3_BOARD("030-1025-", &ip29_baseio_info),
+	IOC3_BOARD("030-1244-", &ip29_baseio_info),
+	IOC3_BOARD("030-1389-", &ip29_baseio_info),
+	IOC3_BOARD("030-0880-", &ip27_mio_info),
+#endif
+	IOC3_BOARD("030-0873-", &ioc3_menet_info),
+	IOC3_BOARD("030-1155-", &ioc3_cad_duo_info),
+};
+
+static int ioc3_identify(struct ioc3_priv_data *idp)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ioc3_boards); i++)
+		if (!strncmp(idp->nic_part, ioc3_boards[i].match,
+			     strlen(ioc3_boards[i].match))) {
+			idp->info = ioc3_boards[i].info;
+			return 0;
+		}
+
+	return -1;
Please return a proper Linux error code.
+}
+
+static void ioc3_create_devices(struct ioc3_priv_data *ipd)
+{
+	struct mfd_cell *cell;
+
+	memset(ioc3_mfd_cells, 0, sizeof(ioc3_mfd_cells));
+	cell = ioc3_mfd_cells;
+
+	if (ipd->info->funcs & IOC3_ETH) {
+		memcpy(ioc3_eth_platform_data.mac_addr, ipd->nic_mac,
+		       sizeof(ioc3_eth_platform_data.mac_addr));
Better to pull the MAC address from within the Ethernet driver.
+		cell->name = "ioc3-eth";
+		cell->id = ioc3_eth_id++;
+		cell->resources = ioc3_eth_resources;
+		cell->num_resources = ARRAY_SIZE(ioc3_eth_resources);
+		cell->platform_data = &ioc3_eth_platform_data;
+		cell->pdata_size = sizeof(ioc3_eth_platform_data);
Please define all of this in a static struct, external to this
function.
+		if (ipd->info->irq_offset) {
+			/*
+			 * Ethernet interrupt is on an extra interrupt
+			 * not inside the irq domain, so we need an
+			 * extra mfd_add_devices without the domain
+			 * argument
+			 */
+			ioc3_eth_resources[2].start = ipd->pdev->irq;
+			ioc3_eth_resources[2].end = ipd->pdev->irq;
Using '2' is fragile.

In this case, seeing as you're using the parent's IRQ, you can obtain
the IRQ using the usual platform_*() handlers, using pdev->parent.
+			mfd_add_devices(&ipd->pdev->dev, -1, cell, 1,
Don't use -1, use the defines instead.

Instead of 1, use ARRAY_SIZE() once the cells are defined statically.
+					&ipd->pdev->resource[0], 0, NULL);
+			memset(cell, 0, sizeof(*cell));
+		} else {
+			/* fake hwirq in domain */
What does this mean?
+			ioc3_eth_resources[2].start = 23;
+			ioc3_eth_resources[2].end = 23;
+			cell++;
+		}
+	}
+	if (ipd->info->funcs & IOC3_SER) {
+		writel(GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL,
+			&ipd->regs->gpcr_s);
+		writel(0, &ipd->regs->gppr[6]);
+		writel(0, &ipd->regs->gppr[7]);
+		udelay(100);
+		writel(readl(&ipd->regs->port_a.sscr) & ~SSCR_DMA_EN,
+		       &ipd->regs->port_a.sscr);
+		writel(readl(&ipd->regs->port_b.sscr) & ~SSCR_DMA_EN,
+		       &ipd->regs->port_b.sscr);
+		udelay(1000);
No idea what any of this does.

It looks like it belongs in the serial driver (and needs comments).
+		cell->name = "ioc3-serial8250";
+		cell->id = ioc3_serial_id++;
+		cell->resources = ioc3_uarta_resources;
+		cell->num_resources = ARRAY_SIZE(ioc3_uarta_resources);
+		cell++;
+		cell->name = "ioc3-serial8250";
+		cell->id = ioc3_serial_id++;
+		cell->resources = ioc3_uartb_resources;
+		cell->num_resources = ARRAY_SIZE(ioc3_uartb_resources);
+		cell++;
Please define this statically.
+	}
+	if (ipd->info->funcs & IOC3_KBD) {
+		cell->name = "ioc3-kbd",
+		cell->id = ioc3_kbd_id++;
+		cell->resources = ioc3_kbd_resources,
+		cell->num_resources = ARRAY_SIZE(ioc3_kbd_resources),
+		cell++;
As above.
+	}
+#if defined(CONFIG_SGI_IP27)
What is this?  Can't you obtain this dynamically by probing the H/W?
+	if (ipd->info->funcs & IOC3_M48T35) {
+		cell->name = "rtc-m48t35";
+		cell->id = -1;
+		cell->resources = ioc3_rtc_resources;
+		cell->num_resources = ARRAY_SIZE(ioc3_rtc_resources);
+		cell++;
Static please.
+	}
+#endif
+	mfd_add_devices(&ipd->pdev->dev, -1, ioc3_mfd_cells,
Use the defines.
+			cell - ioc3_mfd_cells, &ipd->pdev->resource[0],
?
+			0, ipd->domain);
+}
+
+static int ioc3_mfd_probe(struct pci_dev *pdev,
+			  const struct pci_device_id *pci_id)
+{
+	struct ioc3_priv_data *ipd;
+	int err, ret = -ENODEV, io_irqno;
+	struct ioc3 __iomem *regs;
+	struct irq_domain *domain;
+	struct fwnode_handle *fn;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+	pci_set_master(pdev);
+
+	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+	if (ret) {
+		dev_warn(&pdev->dev, "Warning: couldn_t set 64-bit DMA mask\n");
+		ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+		if (ret) {
+			dev_err(&pdev->dev, "Can't set DMA mask, aborting\n");
+			return ret;
+		}
+	}
+
+	/* Set up per-IOC3 data */
+	ipd = kzalloc(sizeof(struct ioc3_priv_data), GFP_KERNEL);
Does PCI not provide managed (devm_*() like) helpers?
+	if (!ipd) {
+		ret = -ENOMEM;
+		goto out_disable_device;
+	}
+	ipd->pdev = pdev;
'\n'
+	/*
+	 * Map all IOC3 registers.  These are shared between subdevices
+	 * so the main IOC3 module manages them.
+	 */
+	regs = pci_ioremap_bar(pdev, 0);
+	if (!regs) {
+		pr_warn("ioc3: Unable to remap PCI BAR for %s.\n",
+			pci_name(pdev));
+		goto out_free_ipd;
+	}
+	ipd->regs = regs;
+
+	/* Track PCI-device specific data */
+	pci_set_drvdata(pdev, ipd);
Do you don't need 'ipd->pdev = pdev' then.
+	writel(GPCR_MLAN_EN, &ipd->regs->gpcr_s);
+	ioc3_probe_nic(ipd, &regs->mcr);
This should probably be moved to the networking driver.
+#ifdef CONFIG_SGI_IP27
No #ifery in MFD c-files please.
+	/* BaseIO NIC is attached to bridge */
+	if (!ipd->nic_part[0]) {
+		struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus);
+
+		ioc3_probe_nic(ipd, &bc->base->b_nic);
+	}
+#endif
+
+	if (ioc3_identify(ipd)) {
+		pr_err("ioc3: part: [%s] unknown card\n", ipd->nic_part);
+		goto out_iounmap;
+	}
+
+	pr_info("ioc3: part: [%s] %s\n", ipd->nic_part, ipd->info->name);
+
+	/* Clear IRQs */
A comment!  I just fell off my chair! =;-)
+	writel(~0, &regs->sio_iec);
+	writel(~0, &regs->sio_ir);
+	writel(0, &regs->eth.eier);
+	writel(~0, &regs->eth.eisr);
+
+	if (ipd->info->irq_offset) {
What does this really signify?
+		struct pci_host_bridge *hbrg = pci_find_host_bridge(pdev->bus);
+
+		io_irqno = hbrg->map_irq(pdev,
+			PCI_SLOT(pdev->devfn) + ipd->info->irq_offset, 0);
+	} else {
+		io_irqno = pdev->irq;
+	}
+	ipd->irq_io = io_irqno;
+
+	fn = irq_domain_alloc_named_fwnode("IOC3");
+	if (!fn)
+		goto out_iounmap;
+
+	domain = irq_domain_create_linear(fn, 24, &ioc3_irq_domain_ops, ipd);
+	irq_domain_free_fwnode(fn);
+	if (!domain)
+		goto out_iounmap;
+	ipd->domain = domain;
+
+	irq_set_chained_handler_and_data(io_irqno, ioc3_irq_handler, domain);
+
+	ioc3_create_devices(ipd);
+
+	return 0;
+
+out_iounmap:
+	iounmap(ipd->regs);
+
+out_free_ipd:
+	kfree(ipd);
+
+out_disable_device:
+	pci_disable_device(pdev);
+	return ret;
+}
+
+static void ioc3_mfd_remove(struct pci_dev *pdev)
+{
+	struct ioc3_priv_data *ipd;
+
+	ipd = pci_get_drvdata(pdev);
+
+	/* Clear and disable all IRQs */
+	writel(~0, &ipd->regs->sio_iec);
+	writel(~0, &ipd->regs->sio_ir);
+
+	/* Release resources */
+	irq_domain_remove(ipd->domain);
+	free_irq(ipd->irq_io, (void *)ipd);
+	iounmap(ipd->regs);
+
+	pci_disable_device(pdev);
+
+	kfree(ipd);
+}
+
+static struct pci_device_id ioc3_mfd_id_table[] = {
+	{ PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0, },
+};
+MODULE_DEVICE_TABLE(pci, ioc3_mfd_id_table);
+
+static struct pci_driver ioc3_mfd_driver = {
+	.name = "IOC3",
+	.id_table = ioc3_mfd_id_table,
+	.probe = ioc3_mfd_probe,
+	.remove = ioc3_mfd_remove,
+};
+
+module_pci_driver(ioc3_mfd_driver);
+
+MODULE_AUTHOR("Thomas Bogendoerfer [off-list ref]");
+MODULE_DESCRIPTION("SGI IOC3 MFD driver");
+MODULE_LICENSE("GPL");
-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help