Commit c971f11d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull regmap updates from Mark Brown:
 "This release we have one new feature, support for chips that report
  edge interrupts but don't provide distinct readback of that status per
  line, plus a few cleanups"

* tag 'regmap-v6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: irq: Add support for chips without separate IRQ status
  regmap-irq: Use dedicated interrupt wake setters
  regmap: Move selecting for REGMAP_MDIO and REGMAP_IRQ
  regcache: Use sort()'s default swap() implementation
parents aacc73ce 1c12fbdf
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -6,8 +6,6 @@
config REGMAP
	bool
	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI)
	select IRQ_DOMAIN if REGMAP_IRQ
	select MDIO_BUS if REGMAP_MDIO
	help
	  Enable support for the Register Map (regmap) access API.

@@ -58,12 +56,14 @@ config REGMAP_W1

config REGMAP_MDIO
	tristate
	select MDIO_BUS

config REGMAP_MMIO
	tristate

config REGMAP_IRQ
	bool
	select IRQ_DOMAIN

config REGMAP_RAM
	tristate
+1 −12
Original line number Diff line number Diff line
@@ -34,21 +34,10 @@ static int regcache_defaults_cmp(const void *a, const void *b)
		return 0;
}

static void regcache_defaults_swap(void *a, void *b, int size)
{
	struct reg_default *x = a;
	struct reg_default *y = b;
	struct reg_default tmp;

	tmp = *x;
	*x = *y;
	*y = tmp;
}

void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults)
{
	sort(defaults, ndefaults, sizeof(*defaults),
	     regcache_defaults_cmp, regcache_defaults_swap);
	     regcache_defaults_cmp, NULL);
}
EXPORT_SYMBOL_GPL(regcache_sort_defaults);

+70 −33
Original line number Diff line number Diff line
@@ -6,11 +6,13 @@
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>

#include <linux/array_size.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/overflow.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -33,6 +35,7 @@ struct regmap_irq_chip_data {
	void *status_reg_buf;
	unsigned int *main_status_buf;
	unsigned int *status_buf;
	unsigned int *prev_status_buf;
	unsigned int *mask_buf;
	unsigned int *mask_buf_def;
	unsigned int *wake_buf;
@@ -193,10 +196,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
	/* If we've changed our wakeup count propagate it to the parent */
	if (d->wake_count < 0)
		for (i = d->wake_count; i < 0; i++)
			irq_set_irq_wake(d->irq, 0);
			disable_irq_wake(d->irq);
	else if (d->wake_count > 0)
		for (i = 0; i < d->wake_count; i++)
			irq_set_irq_wake(d->irq, 1);
			enable_irq_wake(d->irq);

	d->wake_count = 0;

@@ -332,27 +335,13 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
	return ret;
}

static irqreturn_t regmap_irq_thread(int irq, void *d)
static int read_irq_data(struct regmap_irq_chip_data *data)
{
	struct regmap_irq_chip_data *data = d;
	const struct regmap_irq_chip *chip = data->chip;
	struct regmap *map = data->map;
	int ret, i;
	bool handled = false;
	u32 reg;

	if (chip->handle_pre_irq)
		chip->handle_pre_irq(chip->irq_drv_data);

	if (chip->runtime_pm) {
		ret = pm_runtime_get_sync(map->dev);
		if (ret < 0) {
			dev_err(map->dev, "IRQ thread failed to resume: %d\n",
				ret);
			goto exit;
		}
	}

	/*
	 * Read only registers with active IRQs if the chip has 'main status
	 * register'. Else read in the statuses, using a single bulk read if
@@ -379,10 +368,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
			reg = data->get_irq_reg(data, chip->main_status, i);
			ret = regmap_read(map, reg, &data->main_status_buf[i]);
			if (ret) {
				dev_err(map->dev,
					"Failed to read IRQ status %d\n",
					ret);
				goto exit;
				dev_err(map->dev, "Failed to read IRQ status %d\n", ret);
				return ret;
			}
		}

@@ -398,10 +385,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
				ret = read_sub_irq_data(data, b);

				if (ret != 0) {
					dev_err(map->dev,
						"Failed to read IRQ status %d\n",
						ret);
					goto exit;
					dev_err(map->dev, "Failed to read IRQ status %d\n", ret);
					return ret;
				}
			}

@@ -418,9 +403,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
				       data->status_reg_buf,
				       chip->num_regs);
		if (ret != 0) {
			dev_err(map->dev, "Failed to read IRQ status: %d\n",
				ret);
			goto exit;
			dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
			return ret;
		}

		for (i = 0; i < data->chip->num_regs; i++) {
@@ -436,7 +420,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
				break;
			default:
				BUG();
				goto exit;
				return -EIO;
			}
		}

@@ -447,10 +431,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
			ret = regmap_read(map, reg, &data->status_buf[i]);

			if (ret != 0) {
				dev_err(map->dev,
					"Failed to read IRQ status: %d\n",
					ret);
				goto exit;
				dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
				return ret;
			}
		}
	}
@@ -459,6 +441,42 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
		for (i = 0; i < data->chip->num_regs; i++)
			data->status_buf[i] = ~data->status_buf[i];

	return 0;
}

static irqreturn_t regmap_irq_thread(int irq, void *d)
{
	struct regmap_irq_chip_data *data = d;
	const struct regmap_irq_chip *chip = data->chip;
	struct regmap *map = data->map;
	int ret, i;
	bool handled = false;
	u32 reg;

	if (chip->handle_pre_irq)
		chip->handle_pre_irq(chip->irq_drv_data);

	if (chip->runtime_pm) {
		ret = pm_runtime_get_sync(map->dev);
		if (ret < 0) {
			dev_err(map->dev, "IRQ thread failed to resume: %d\n", ret);
			goto exit;
		}
	}

	ret = read_irq_data(data);
	if (ret < 0)
		goto exit;

	if (chip->status_is_level) {
		for (i = 0; i < data->chip->num_regs; i++) {
			unsigned int val = data->status_buf[i];

			data->status_buf[i] ^= data->prev_status_buf[i];
			data->prev_status_buf[i] = val;
		}
	}

	/*
	 * Ignore masked IRQs and ack if we need to; we ack early so
	 * there is no race between handling and acknowledging the
@@ -705,6 +723,13 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
	if (!d->status_buf)
		goto err_alloc;

	if (chip->status_is_level) {
		d->prev_status_buf = kcalloc(chip->num_regs, sizeof(*d->prev_status_buf),
					     GFP_KERNEL);
		if (!d->prev_status_buf)
			goto err_alloc;
	}

	d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf),
			      GFP_KERNEL);
	if (!d->mask_buf)
@@ -881,6 +906,16 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
		}
	}

	/* Store current levels */
	if (chip->status_is_level) {
		ret = read_irq_data(d);
		if (ret < 0)
			goto err_alloc;

		memcpy(d->prev_status_buf, d->status_buf,
		       array_size(d->chip->num_regs, sizeof(d->prev_status_buf[0])));
	}

	ret = regmap_irq_create_domain(fwnode, irq_base, chip, d);
	if (ret)
		goto err_alloc;
@@ -908,6 +943,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
	kfree(d->mask_buf);
	kfree(d->main_status_buf);
	kfree(d->status_buf);
	kfree(d->prev_status_buf);
	kfree(d->status_reg_buf);
	if (d->config_buf) {
		for (i = 0; i < chip->num_config_bases; i++)
@@ -985,6 +1021,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
	kfree(d->main_status_buf);
	kfree(d->status_reg_buf);
	kfree(d->status_buf);
	kfree(d->prev_status_buf);
	if (d->config_buf) {
		for (i = 0; i < d->chip->num_config_bases; i++)
			kfree(d->config_buf[i]);
+3 −0
Original line number Diff line number Diff line
@@ -1641,6 +1641,8 @@ struct regmap_irq_chip_data;
 * @ack_invert:  Inverted ack register: cleared bits for ack.
 * @clear_ack:  Use this to set 1 and 0 or vice-versa to clear interrupts.
 * @status_invert: Inverted status register: cleared bits are active interrupts.
 * @status_is_level: Status register is actuall signal level: Xor status
 *		     register with previous value to get active interrupts.
 * @wake_invert: Inverted wake register: cleared bits are wake enabled.
 * @type_in_mask: Use the mask registers for controlling irq type. Use this if
 *		  the hardware provides separate bits for rising/falling edge
@@ -1704,6 +1706,7 @@ struct regmap_irq_chip {
	unsigned int ack_invert:1;
	unsigned int clear_ack:1;
	unsigned int status_invert:1;
	unsigned int status_is_level:1;
	unsigned int wake_invert:1;
	unsigned int type_in_mask:1;
	unsigned int clear_on_unmask:1;