Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f620837 authored by Linus Walleij's avatar Linus Walleij Committed by Samuel Ortiz
Browse files

mfd: Add ab8500 version detection and enforcing



There are currently four different versions of the AB8500
around: AB8500, AB8505, AB9540 and AB8540. Unfortunately:

- Some of the chips (AB8500, AB8505, AB9540) cannot read
  the AB8500_REV_REG register but return errors

- Some of them have the same ID value in the hardware
  register AB8500_REV_REV, for example the first versions
  of AB8505 and AB9540 have 0xFF in this register -
  just like the AB8500.

So we need to be able to enforce a certain version from
the platform. We do this by using the id of the platform
device that provides the read/write functions.

Reviewed-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarMaxime Coquelin <maxime.coquelin@stericsson.com>
Signed-off-by: default avatarAlex Macro <alex.macro@stericsson.com>
Signed-off-by: default avatarMichel Jaouen <michel.jaouen@stericsson.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent 6d95b7fd
Loading
Loading
Loading
Loading
+42 −21
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@
#define AB8500_IT_MASK24_REG		0x57

#define AB8500_REV_REG			0x80
#define AB8500_IC_NAME_REG		0x82
#define AB8500_SWITCH_OFF_STATUS	0x00

#define AB8500_TURN_ON_STATUS		0x00
@@ -105,6 +106,13 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
	0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
};

static const char ab8500_version_str[][7] = {
	[AB8500_VERSION_AB8500] = "AB8500",
	[AB8500_VERSION_AB8505] = "AB8505",
	[AB8500_VERSION_AB9540] = "AB9540",
	[AB8500_VERSION_AB8540] = "AB8540",
};

static int ab8500_get_chip_id(struct device *dev)
{
	struct ab8500 *ab8500;
@@ -256,9 +264,12 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
		if (new == old)
			continue;

		/* Interrupt register 12 doesn't exist prior to version 2.0 */
		if (ab8500_irq_regoffset[i] == 11 &&
			ab8500->chip_id < AB8500_CUT2P0)
		/*
		 * Interrupt register 12 doesn't exist prior to AB8500 version
		 * 2.0
		 */
		if (ab8500->irq_reg_offset[i] == 11 &&
			is_ab8500_1p1_or_earlier(ab8500))
			continue;

		ab8500->oldmask[i] = new;
@@ -311,8 +322,11 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
		int status;
		u8 value;

		/* Interrupt register 12 doesn't exist prior to version 2.0 */
		if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0)
		/*
		 * Interrupt register 12 doesn't exist prior to AB8500 version
		 * 2.0
		 */
		if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))
			continue;

		status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
@@ -857,7 +871,7 @@ static struct attribute_group ab8500_attr_group = {
	.attrs	= ab8500_sysfs_entries,
};

int __devinit ab8500_init(struct ab8500 *ab8500)
int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
{
	struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
	int ret;
@@ -870,25 +884,29 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
	mutex_init(&ab8500->lock);
	mutex_init(&ab8500->irq_lock);

	if (version != AB8500_VERSION_UNDEFINED)
		ab8500->version = version;
	else {
		ret = get_register_interruptible(ab8500, AB8500_MISC,
		AB8500_REV_REG, &value);
			AB8500_IC_NAME_REG, &value);
		if (ret < 0)
			return ret;

	switch (value) {
	case AB8500_CUT1P0:
	case AB8500_CUT1P1:
	case AB8500_CUT2P0:
	case AB8500_CUT3P0:
	case AB8500_CUT3P3:
		dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
		break;
	default:
		dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value);
		return -EINVAL;
		ab8500->version = value;
	}

	ret = get_register_interruptible(ab8500, AB8500_MISC,
		AB8500_REV_REG, &value);
	if (ret < 0)
		return ret;

	ab8500->chip_id = value;

	dev_info(ab8500->dev, "detected chip, %s rev. %1x.%1x\n",
			ab8500_version_str[ab8500->version],
			ab8500->chip_id >> 4,
			ab8500->chip_id & 0x0F);

	/*
	 * ab8500 has switched off due to (SWITCH_OFF_STATUS):
	 * 0x01 Swoff bit programming
@@ -912,9 +930,12 @@ int __devinit ab8500_init(struct ab8500 *ab8500)

	/* Clear and mask all interrupts */
	for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
		/* Interrupt register 12 doesn't exist prior to version 2.0 */
		if (ab8500_irq_regoffset[i] == 11 &&
			ab8500->chip_id < AB8500_CUT2P0)
		/*
		 * Interrupt register 12 doesn't exist prior to AB8500 version
		 * 2.0
		 */
		if (ab8500->irq_reg_offset[i] == 11 &&
				is_ab8500_1p1_or_earlier(ab8500))
			continue;

		get_register_interruptible(ab8500, AB8500_INTERRUPT,
+13 −2
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)

static int __devinit ab8500_i2c_probe(struct platform_device *plf)
{
	const struct platform_device_id *platid = platform_get_device_id(plf);
	struct ab8500 *ab8500;
	struct resource *resource;
	int ret;
@@ -61,10 +62,11 @@ static int __devinit ab8500_i2c_probe(struct platform_device *plf)

	platform_set_drvdata(plf, ab8500);

	ret = ab8500_init(ab8500);
	ret = ab8500_init(ab8500, platid->driver_data);
	if (ret)
		kfree(ab8500);


	return ret;
}

@@ -78,13 +80,22 @@ static int __devexit ab8500_i2c_remove(struct platform_device *plf)
	return 0;
}

static const struct platform_device_id ab8500_id[] = {
	{ "ab8500-i2c", AB8500_VERSION_AB8500 },
	{ "ab8505-i2c", AB8500_VERSION_AB8505 },
	{ "ab9540-i2c", AB8500_VERSION_AB9540 },
	{ "ab8540-i2c", AB8500_VERSION_AB8540 },
	{ }
};

static struct platform_driver ab8500_i2c_driver = {
	.driver = {
		.name = "ab8500-i2c",
		.owner = THIS_MODULE,
	},
	.probe	= ab8500_i2c_probe,
	.remove	= __devexit_p(ab8500_i2c_remove)
	.remove	= __devexit_p(ab8500_i2c_remove),
	.id_table = ab8500_id,
};

static int __init ab8500_i2c_init(void)
+0 −7
Original line number Diff line number Diff line
@@ -33,13 +33,6 @@
#define AB5500_1_1	0x21
#define AB5500_2_0	0x24

/* AB8500 CIDs*/
#define AB8500_CUT1P0	0x10
#define AB8500_CUT1P1	0x11
#define AB8500_CUT2P0	0x20
#define AB8500_CUT3P0	0x30
#define AB8500_CUT3P3	0x33

/*
 * AB3100, EVENTA1, A2 and A3 event register flags
 * these are catenated into a single 32-bit flag in the code
+58 −1
Original line number Diff line number Diff line
@@ -8,6 +8,28 @@
#define MFD_AB8500_H

#include <linux/device.h>
/*
 * AB IC versions
 *
 * AB8500_VERSION_AB8500 should be 0xFF but will never be read as need a
 * non-supported multi-byte I2C access via PRCMU. Set to 0x00 to ease the
 * print of version string.
 */
enum ab8500_version {
	AB8500_VERSION_AB8500 = 0x0,
	AB8500_VERSION_AB8505 = 0x1,
	AB8500_VERSION_AB9540 = 0x2,
	AB8500_VERSION_AB8540 = 0x3,
	AB8500_VERSION_UNDEFINED,
};

/* AB8500 CIDs*/
#define AB8500_CUTEARLY	0x00
#define AB8500_CUT1P0	0x10
#define AB8500_CUT1P1	0x11
#define AB8500_CUT2P0	0x20
#define AB8500_CUT3P0	0x30
#define AB8500_CUT3P3	0x33

/*
 * AB8500 bank addresses
@@ -145,6 +167,7 @@
 * @lock: read/write operations lock
 * @irq_lock: genirq bus lock
 * @irq: irq line
 * @version: chip version id (e.g. ab8500 or ab9540)
 * @chip_id: chip revision id
 * @write: register write
 * @read: register read
@@ -160,6 +183,7 @@ struct ab8500 {

	int		irq_base;
	int		irq;
	enum ab8500_version version;
	u8		chip_id;

	int (*write) (struct ab8500 *a8500, u16 addr, u8 data);
@@ -195,7 +219,40 @@ struct ab8500_platform_data {
	struct ab8500_gpio_platform_data *gpio;
};

extern int __devinit ab8500_init(struct ab8500 *ab8500);
extern int __devinit ab8500_init(struct ab8500 *ab8500,
				 enum ab8500_version version);
extern int __devexit ab8500_exit(struct ab8500 *ab8500);

static inline int is_ab8500(struct ab8500 *ab)
{
	return ab->version == AB8500_VERSION_AB8500;
}

static inline int is_ab8505(struct ab8500 *ab)
{
	return ab->version == AB8500_VERSION_AB8505;
}

static inline int is_ab9540(struct ab8500 *ab)
{
	return ab->version == AB8500_VERSION_AB9540;
}

static inline int is_ab8540(struct ab8500 *ab)
{
	return ab->version == AB8500_VERSION_AB8540;
}

/* include also ab8505, ab9540... */
static inline int is_ab8500_1p1_or_earlier(struct ab8500 *ab)
{
	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT1P1));
}

/* include also ab8505, ab9540... */
static inline int is_ab8500_2p0_or_earlier(struct ab8500 *ab)
{
	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT2P0));
}

#endif /* MFD_AB8500_H */