static int thunderx_gpio_probe()

in gpio-thunderx.c [425:568]


static int thunderx_gpio_probe(struct pci_dev *pdev,
			       const struct pci_device_id *id)
{
	void __iomem * const *tbl;
	struct device *dev = &pdev->dev;
	struct thunderx_gpio *txgpio;
	struct gpio_chip *chip;
	struct gpio_irq_chip *girq;
	int ngpio, i;
	int err = 0;

	txgpio = devm_kzalloc(dev, sizeof(*txgpio), GFP_KERNEL);
	if (!txgpio)
		return -ENOMEM;

	raw_spin_lock_init(&txgpio->lock);
	chip = &txgpio->chip;

	pci_set_drvdata(pdev, txgpio);

	err = pcim_enable_device(pdev);
	if (err) {
		dev_err(dev, "Failed to enable PCI device: err %d\n", err);
		goto out;
	}

	err = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
	if (err) {
		dev_err(dev, "Failed to iomap PCI device: err %d\n", err);
		goto out;
	}

	tbl = pcim_iomap_table(pdev);
	txgpio->register_base = tbl[0];
	if (!txgpio->register_base) {
		dev_err(dev, "Cannot map PCI resource\n");
		err = -ENOMEM;
		goto out;
	}

	if (pdev->subsystem_device == 0xa10a) {
		/* CN88XX has no GPIO_CONST register*/
		ngpio = 50;
		txgpio->base_msi = 48;
	} else {
		u64 c = readq(txgpio->register_base + GPIO_CONST);

		ngpio = c & GPIO_CONST_GPIOS_MASK;
		txgpio->base_msi = (c >> 8) & 0xff;
	}

	txgpio->msix_entries = devm_kcalloc(dev,
					    ngpio, sizeof(struct msix_entry),
					    GFP_KERNEL);
	if (!txgpio->msix_entries) {
		err = -ENOMEM;
		goto out;
	}

	txgpio->line_entries = devm_kcalloc(dev,
					    ngpio,
					    sizeof(struct thunderx_line),
					    GFP_KERNEL);
	if (!txgpio->line_entries) {
		err = -ENOMEM;
		goto out;
	}

	for (i = 0; i < ngpio; i++) {
		u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(i));

		txgpio->msix_entries[i].entry = txgpio->base_msi + (2 * i);
		txgpio->line_entries[i].line = i;
		txgpio->line_entries[i].txgpio = txgpio;
		/*
		 * If something has already programmed the pin, use
		 * the existing glitch filter settings, otherwise go
		 * to 400nS.
		 */
		txgpio->line_entries[i].fil_bits = bit_cfg ?
			(bit_cfg & GPIO_BIT_CFG_FIL_MASK) : GLITCH_FILTER_400NS;

		if ((bit_cfg & GPIO_BIT_CFG_TX_OE) && (bit_cfg & GPIO_BIT_CFG_TX_OD))
			set_bit(i, txgpio->od_mask);
		if (bit_cfg & GPIO_BIT_CFG_PIN_XOR)
			set_bit(i, txgpio->invert_mask);
	}


	/* Enable all MSI-X for interrupts on all possible lines. */
	err = pci_enable_msix_range(pdev, txgpio->msix_entries, ngpio, ngpio);
	if (err < 0)
		goto out;

	chip->label = KBUILD_MODNAME;
	chip->parent = dev;
	chip->owner = THIS_MODULE;
	chip->request = thunderx_gpio_request;
	chip->base = -1; /* System allocated */
	chip->can_sleep = false;
	chip->ngpio = ngpio;
	chip->get_direction = thunderx_gpio_get_direction;
	chip->direction_input = thunderx_gpio_dir_in;
	chip->get = thunderx_gpio_get;
	chip->direction_output = thunderx_gpio_dir_out;
	chip->set = thunderx_gpio_set;
	chip->set_multiple = thunderx_gpio_set_multiple;
	chip->set_config = thunderx_gpio_set_config;
	girq = &chip->irq;
	girq->chip = &thunderx_gpio_irq_chip;
	girq->fwnode = of_node_to_fwnode(dev->of_node);
	girq->parent_domain =
		irq_get_irq_data(txgpio->msix_entries[0].vector)->domain;
	girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq;
	girq->populate_parent_alloc_arg = thunderx_gpio_populate_parent_alloc_info;
	girq->handler = handle_bad_irq;
	girq->default_type = IRQ_TYPE_NONE;

	err = devm_gpiochip_add_data(dev, chip, txgpio);
	if (err)
		goto out;

	/* Push on irq_data and the domain for each line. */
	for (i = 0; i < ngpio; i++) {
		struct irq_fwspec fwspec;

		fwspec.fwnode = of_node_to_fwnode(dev->of_node);
		fwspec.param_count = 2;
		fwspec.param[0] = i;
		fwspec.param[1] = IRQ_TYPE_NONE;
		err = irq_domain_push_irq(girq->domain,
					  txgpio->msix_entries[i].vector,
					  &fwspec);
		if (err < 0)
			dev_err(dev, "irq_domain_push_irq: %d\n", err);
	}

	dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n",
		 ngpio, chip->base);
	return 0;
out:
	pci_set_drvdata(pdev, NULL);
	return err;
}