예제 #1
0
/**
 * xgpiops_set_irq_type - Set the irq type for a gpio pin
 * @irq_data:	irq data containing irq number of gpio pin
 * @type:	interrupt type that is to be set for the gpio pin
 *
 * This function gets the gpio pin number and its bank from the gpio pin number
 * and configures the INT_TYPE, INT_POLARITY and INT_ANY registers. Returns 0,
 * negative error otherwise.
 * TYPE-EDGE_RISING,  INT_TYPE - 1, INT_POLARITY - 1,  INT_ANY - 0;
 * TYPE-EDGE_FALLING, INT_TYPE - 1, INT_POLARITY - 0,  INT_ANY - 0;
 * TYPE-EDGE_BOTH,    INT_TYPE - 1, INT_POLARITY - NA, INT_ANY - 1;
 * TYPE-LEVEL_HIGH,   INT_TYPE - 0, INT_POLARITY - 1,  INT_ANY - NA;
 * TYPE-LEVEL_LOW,    INT_TYPE - 0, INT_POLARITY - 0,  INT_ANY - NA
 */
static int xgpiops_set_irq_type(struct irq_data *irq_data, unsigned int type)
{
	struct xgpiops *gpio = irq_data_get_irq_chip_data(irq_data);
	unsigned int device_pin_num, bank_num, bank_pin_num;
	unsigned int int_type, int_pol, int_any;

	device_pin_num = irq_data->hwirq;
	xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);

	int_type = xgpiops_readreg(gpio->base_addr +
				    XGPIOPS_INTTYPE_OFFSET(bank_num));
	int_pol = xgpiops_readreg(gpio->base_addr +
				   XGPIOPS_INTPOL_OFFSET(bank_num));
	int_any = xgpiops_readreg(gpio->base_addr +
				   XGPIOPS_INTANY_OFFSET(bank_num));

	/*
	 * based on the type requested, configure the INT_TYPE, INT_POLARITY
	 * and INT_ANY registers
	 */
	switch (type) {
	case IRQ_TYPE_EDGE_RISING:
		int_type |= (1 << bank_pin_num);
		int_pol |= (1 << bank_pin_num);
		int_any &= ~(1 << bank_pin_num);
		break;
	case IRQ_TYPE_EDGE_FALLING:
		int_type |= (1 << bank_pin_num);
		int_pol &= ~(1 << bank_pin_num);
		int_any &= ~(1 << bank_pin_num);
		break;
	case IRQ_TYPE_EDGE_BOTH:
		int_type |= (1 << bank_pin_num);
		int_any |= (1 << bank_pin_num);
		break;
	case IRQ_TYPE_LEVEL_HIGH:
		int_type &= ~(1 << bank_pin_num);
		int_pol |= (1 << bank_pin_num);
		break;
	case IRQ_TYPE_LEVEL_LOW:
		int_type &= ~(1 << bank_pin_num);
		int_pol &= ~(1 << bank_pin_num);
		break;
	default:
		return -EINVAL;
	}

	xgpiops_writereg(int_type,
			  gpio->base_addr + XGPIOPS_INTTYPE_OFFSET(bank_num));
	xgpiops_writereg(int_pol,
			  gpio->base_addr + XGPIOPS_INTPOL_OFFSET(bank_num));
	xgpiops_writereg(int_any,
			  gpio->base_addr + XGPIOPS_INTANY_OFFSET(bank_num));
	return 0;
}
예제 #2
0
/**
 * xgpiops_set_value - Modify the state of the pin with specified value
 * @chip:	gpio_chip instance to be worked on
 * @pin:	gpio pin number within the device
 * @state:	value used to modify the state of the specified pin
 *
 * This function calculates the register offset (i.e to lower 16 bits or
 * upper 16 bits) based on the given pin number and sets the state of a
 * gpio pin to the specified value. The state is either 0 or non-zero.
 */
static void xgpiops_set_value(struct gpio_chip *chip, unsigned int pin,
			       int state)
{
	unsigned long flags;
	unsigned int reg_offset;
	unsigned int bank_num, bank_pin_num;
	struct xgpiops *gpio = container_of(chip, struct xgpiops, chip);

	xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num);

	if (bank_pin_num >= 16) {
		bank_pin_num -= 16; /* only 16 data bits in bit maskable reg */
		reg_offset = XGPIOPS_DATA_MSW_OFFSET(bank_num);
	} else {
		reg_offset = XGPIOPS_DATA_LSW_OFFSET(bank_num);
	}

	/*
	 * get the 32 bit value to be written to the mask/data register where
	 * the upper 16 bits is the mask and lower 16 bits is the data
	 */
	if (state)
		state = 1;
	state = ~(1 << (bank_pin_num + 16)) & ((state << bank_pin_num) |
					       0xFFFF0000);

	spin_lock_irqsave(&gpio->gpio_lock, flags);
	xgpiops_writereg(state, gpio->base_addr + reg_offset);
	spin_unlock_irqrestore(&gpio->gpio_lock, flags);
}
예제 #3
0
/**
 * xgpiops_irq_unmask - Enable the interrupts for a gpio pin
 * @irq_data: irq data containing irq number of gpio pin for the irq to enable
 *
 * This function calculates the gpio pin number from irq number and sets the
 * bit in the Interrupt Enable register of the corresponding bank to enable
 * interrupts for that pin.
 */
static void xgpiops_irq_unmask(struct irq_data *irq_data)
{
	struct xgpiops *gpio = irq_data_get_irq_chip_data(irq_data);
	unsigned int device_pin_num, bank_num, bank_pin_num;

	device_pin_num = irq_data->hwirq;
	xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
	xgpiops_writereg(1 << bank_pin_num,
			  gpio->base_addr + XGPIOPS_INTEN_OFFSET(bank_num));
}
예제 #4
0
/**
 * xgpiops_irq_mask - Disable the interrupts for a gpio pin
 * @irq:	irq number of gpio pin for which interrupt is to be disabled
 *
 * This function calculates gpio pin number from irq number and sets the
 * bit in the Interrupt Disable register of the corresponding bank to disable
 * interrupts for that pin.
 */
static void xgpiops_irq_mask(struct irq_data *irq_data)
{
	struct xgpiops *gpio = (struct xgpiops *)irq_data_get_irq_chip_data(irq_data);
	unsigned int device_pin_num, bank_num, bank_pin_num;

	device_pin_num = irq_to_gpio(irq_data->irq); /* get pin num within the device */
	xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
	xgpiops_writereg(1 << bank_pin_num,
			  gpio->base_addr + XGPIOPS_INTDIS_OFFSET(bank_num));
}
예제 #5
0
/**
 * xgpiops_dir_out - Set the direction of the specified GPIO pin as output
 * @chip:	gpio_chip instance to be worked on
 * @pin:	gpio pin number within the device
 * @state:	value to be written to specified pin
 *
 * This function sets the direction of specified GPIO pin as output, configures
 * the Output Enable register for the pin and uses xgpiops_set to set the state
 * of the pin to the value specified. Returns 0 always.
 */
static int xgpiops_dir_out(struct gpio_chip *chip, unsigned int pin, int state)
{
	struct xgpiops *gpio = container_of(chip, struct xgpiops, chip);
	unsigned int reg, bank_num, bank_pin_num;

	xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num);

	/* set the GPIO pin as output */
	reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));
	reg |= 1 << bank_pin_num;
	xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));

	/* configure the output enable reg for the pin */
	reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_OUTEN_OFFSET(bank_num));
	reg |= 1 << bank_pin_num;
	xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_OUTEN_OFFSET(bank_num));

	/* set the state of the pin */
	xgpiops_set_value(chip, pin, state);
	return 0;
}
예제 #6
0
/**
 * xgpiops_dir_in - Set the direction of the specified GPIO pin as input
 * @chip:	gpio_chip instance to be worked on
 * @pin:	gpio pin number within the device
 *
 * This function uses the read-modify-write sequence to set the direction of
 * the gpio pin as input. Returns 0 always.
 */
static int xgpiops_dir_in(struct gpio_chip *chip, unsigned int pin)
{
	unsigned int reg, bank_num, bank_pin_num;
	struct xgpiops *gpio = container_of(chip, struct xgpiops, chip);

	xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num);
	/* clear the bit in direction mode reg to set the pin as input */
	reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));
	reg &= ~(1 << bank_pin_num);
	xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));

	return 0;
}
예제 #7
0
/**
 * xgpiops_probe - Initialization method for a xgpiops device
 * @pdev:	platform device instance
 *
 * This function allocates memory resources for the gpio device and registers
 * all the banks of the device. It will also set up interrupts for the gpio
 * pins.
 * Note: Interrupts are disabled for all the banks during initialization.
 * Returns 0 on success, negative error otherwise.
 */
static int xgpiops_probe(struct platform_device *pdev)
{
	int ret;
	unsigned int irq_num;
	struct xgpiops *gpio;
	struct gpio_chip *chip;
	resource_size_t remap_size;
	struct resource *mem_res = NULL;
	int pin_num, bank_num, gpio_irq;

	gpio = kzalloc(sizeof(struct xgpiops), GFP_KERNEL);
	if (!gpio) {
		dev_err(&pdev->dev,
			"couldn't allocate memory for gpio private data\n");
		return -ENOMEM;
	}

	spin_lock_init(&gpio->gpio_lock);

	platform_set_drvdata(pdev, gpio);

	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!mem_res) {
		dev_err(&pdev->dev, "No memory resource\n");
		ret = -ENODEV;
		goto err_free_gpio;
	}

	remap_size = mem_res->end - mem_res->start + 1;
	if (!request_mem_region(mem_res->start, remap_size, pdev->name)) {
		dev_err(&pdev->dev, "Cannot request IO\n");
		ret = -ENXIO;
		goto err_free_gpio;
	}

	gpio->base_addr = ioremap(mem_res->start, remap_size);
	if (gpio->base_addr == NULL) {
		dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n",
			(unsigned long)mem_res->start);
		ret = -ENOMEM;
		goto err_release_region;
	}

	irq_num = platform_get_irq(pdev, 0);
	gpio->irq = irq_num;

	/* configure the gpio chip */
	chip = &gpio->chip;
	chip->label = "xgpiops";
	chip->owner = THIS_MODULE;
	chip->dev = &pdev->dev;
	chip->get = xgpiops_get_value;
	chip->set = xgpiops_set_value;
	chip->request = xgpiops_request;
	chip->free = xgpiops_free;
	chip->direction_input = xgpiops_dir_in;
	chip->direction_output = xgpiops_dir_out;
	chip->to_irq = xgpiops_to_irq;
	chip->dbg_show = NULL;
	chip->base = 0;		/* default pin base */
	chip->ngpio = XGPIOPS_NR_GPIOS;
	chip->can_sleep = 0;

	gpio->irq_base = irq_alloc_descs(-1, 0, chip->ngpio, 0);
	if (gpio->irq_base < 0) {
		dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n");
		ret = -ENODEV;
		goto err_iounmap;
	}

	irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
					   chip->ngpio, gpio->irq_base, 0,
					   &irq_domain_simple_ops, NULL);

	/* report a bug if gpio chip registration fails */
	ret = gpiochip_add(chip);
	if (ret < 0) {
		dev_err(&pdev->dev, "gpio chip registration failed\n");
		goto err_iounmap;
	} else {
		dev_info(&pdev->dev, "gpio at 0x%08lx mapped to 0x%08lx\n",
			 (unsigned long)mem_res->start,
			 (unsigned long)gpio->base_addr);
	}

	/* Enable GPIO clock */
	gpio->clk = clk_get(&pdev->dev, NULL);
	if (IS_ERR(gpio->clk)) {
		dev_err(&pdev->dev, "input clock not found.\n");
		ret = PTR_ERR(gpio->clk);
		goto err_chip_remove;
	}
	ret = clk_prepare_enable(gpio->clk);
	if (ret) {
		dev_err(&pdev->dev, "Unable to enable clock.\n");
		goto err_clk_put;
	}

	/* disable interrupts for all banks */
	for (bank_num = 0; bank_num < 4; bank_num++) {
		xgpiops_writereg(0xffffffff, gpio->base_addr +
				  XGPIOPS_INTDIS_OFFSET(bank_num));
	}

	/*
	 * set the irq chip, handler and irq chip data for callbacks for
	 * each pin
	 */
	for (pin_num = 0; pin_num < min_t(int, XGPIOPS_NR_GPIOS,
						(int)chip->ngpio); pin_num++) {
		gpio_irq = irq_find_mapping(irq_domain, pin_num);
		irq_set_chip_and_handler(gpio_irq, &xgpiops_irqchip,
							handle_simple_irq);
		irq_set_chip_data(gpio_irq, (void *)gpio);
		set_irq_flags(gpio_irq, IRQF_VALID);
	}

	irq_set_handler_data(irq_num, (void *)gpio);
	irq_set_chained_handler(irq_num, xgpiops_irqhandler);

	xgpiops_pm_runtime_init(pdev);

	device_set_wakeup_capable(&pdev->dev, 1);

	return 0;

err_clk_put:
	clk_put(gpio->clk);
err_chip_remove:
	gpiochip_remove(chip);
err_iounmap:
	iounmap(gpio->base_addr);
err_release_region:
	release_mem_region(mem_res->start, remap_size);
err_free_gpio:
	platform_set_drvdata(pdev, NULL);
	kfree(gpio);

	return ret;
}