162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Lochnagar I2C bus interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2012-2018 Cirrus Logic, Inc. and
662306a36Sopenharmony_ci *                         Cirrus Logic International Semiconductor Ltd.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Charles Keepax <ckeepax@opensource.cirrus.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1562306a36Sopenharmony_ci#include <linux/i2c.h>
1662306a36Sopenharmony_ci#include <linux/lockdep.h>
1762306a36Sopenharmony_ci#include <linux/mfd/core.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/of_platform.h>
2162306a36Sopenharmony_ci#include <linux/regmap.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/mfd/lochnagar.h>
2462306a36Sopenharmony_ci#include <linux/mfd/lochnagar1_regs.h>
2562306a36Sopenharmony_ci#include <linux/mfd/lochnagar2_regs.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define LOCHNAGAR_BOOT_RETRIES		10
2862306a36Sopenharmony_ci#define LOCHNAGAR_BOOT_DELAY_MS		350
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define LOCHNAGAR_CONFIG_POLL_US	10000
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic bool lochnagar1_readable_register(struct device *dev, unsigned int reg)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	switch (reg) {
3562306a36Sopenharmony_ci	case LOCHNAGAR_SOFTWARE_RESET:
3662306a36Sopenharmony_ci	case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2:
3762306a36Sopenharmony_ci	case LOCHNAGAR1_CDC_AIF1_SEL...LOCHNAGAR1_CDC_AIF3_SEL:
3862306a36Sopenharmony_ci	case LOCHNAGAR1_CDC_MCLK1_SEL...LOCHNAGAR1_CDC_MCLK2_SEL:
3962306a36Sopenharmony_ci	case LOCHNAGAR1_CDC_AIF_CTRL1...LOCHNAGAR1_CDC_AIF_CTRL2:
4062306a36Sopenharmony_ci	case LOCHNAGAR1_EXT_AIF_CTRL:
4162306a36Sopenharmony_ci	case LOCHNAGAR1_DSP_AIF1_SEL...LOCHNAGAR1_DSP_AIF2_SEL:
4262306a36Sopenharmony_ci	case LOCHNAGAR1_DSP_CLKIN_SEL:
4362306a36Sopenharmony_ci	case LOCHNAGAR1_DSP_AIF:
4462306a36Sopenharmony_ci	case LOCHNAGAR1_GF_AIF1...LOCHNAGAR1_GF_AIF2:
4562306a36Sopenharmony_ci	case LOCHNAGAR1_PSIA_AIF:
4662306a36Sopenharmony_ci	case LOCHNAGAR1_PSIA1_SEL...LOCHNAGAR1_PSIA2_SEL:
4762306a36Sopenharmony_ci	case LOCHNAGAR1_SPDIF_AIF_SEL:
4862306a36Sopenharmony_ci	case LOCHNAGAR1_GF_AIF3_SEL...LOCHNAGAR1_GF_AIF4_SEL:
4962306a36Sopenharmony_ci	case LOCHNAGAR1_GF_CLKOUT1_SEL:
5062306a36Sopenharmony_ci	case LOCHNAGAR1_GF_AIF1_SEL...LOCHNAGAR1_GF_AIF2_SEL:
5162306a36Sopenharmony_ci	case LOCHNAGAR1_GF_GPIO2...LOCHNAGAR1_GF_GPIO7:
5262306a36Sopenharmony_ci	case LOCHNAGAR1_RST:
5362306a36Sopenharmony_ci	case LOCHNAGAR1_LED1...LOCHNAGAR1_LED2:
5462306a36Sopenharmony_ci	case LOCHNAGAR1_I2C_CTRL:
5562306a36Sopenharmony_ci		return true;
5662306a36Sopenharmony_ci	default:
5762306a36Sopenharmony_ci		return false;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic const struct regmap_config lochnagar1_i2c_regmap = {
6262306a36Sopenharmony_ci	.reg_bits = 8,
6362306a36Sopenharmony_ci	.val_bits = 8,
6462306a36Sopenharmony_ci	.reg_format_endian = REGMAP_ENDIAN_BIG,
6562306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_BIG,
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	.max_register = 0x50,
6862306a36Sopenharmony_ci	.readable_reg = lochnagar1_readable_register,
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	.use_single_read = true,
7162306a36Sopenharmony_ci	.use_single_write = true,
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct reg_sequence lochnagar1_patch[] = {
7762306a36Sopenharmony_ci	{ 0x40, 0x0083 },
7862306a36Sopenharmony_ci	{ 0x47, 0x0018 },
7962306a36Sopenharmony_ci	{ 0x50, 0x0000 },
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic bool lochnagar2_readable_register(struct device *dev, unsigned int reg)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	switch (reg) {
8562306a36Sopenharmony_ci	case LOCHNAGAR_SOFTWARE_RESET:
8662306a36Sopenharmony_ci	case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2:
8762306a36Sopenharmony_ci	case LOCHNAGAR2_CDC_AIF1_CTRL...LOCHNAGAR2_CDC_AIF3_CTRL:
8862306a36Sopenharmony_ci	case LOCHNAGAR2_DSP_AIF1_CTRL...LOCHNAGAR2_DSP_AIF2_CTRL:
8962306a36Sopenharmony_ci	case LOCHNAGAR2_PSIA1_CTRL...LOCHNAGAR2_PSIA2_CTRL:
9062306a36Sopenharmony_ci	case LOCHNAGAR2_GF_AIF3_CTRL...LOCHNAGAR2_GF_AIF4_CTRL:
9162306a36Sopenharmony_ci	case LOCHNAGAR2_GF_AIF1_CTRL...LOCHNAGAR2_GF_AIF2_CTRL:
9262306a36Sopenharmony_ci	case LOCHNAGAR2_SPDIF_AIF_CTRL:
9362306a36Sopenharmony_ci	case LOCHNAGAR2_USB_AIF1_CTRL...LOCHNAGAR2_USB_AIF2_CTRL:
9462306a36Sopenharmony_ci	case LOCHNAGAR2_ADAT_AIF_CTRL:
9562306a36Sopenharmony_ci	case LOCHNAGAR2_CDC_MCLK1_CTRL...LOCHNAGAR2_CDC_MCLK2_CTRL:
9662306a36Sopenharmony_ci	case LOCHNAGAR2_DSP_CLKIN_CTRL:
9762306a36Sopenharmony_ci	case LOCHNAGAR2_PSIA1_MCLK_CTRL...LOCHNAGAR2_PSIA2_MCLK_CTRL:
9862306a36Sopenharmony_ci	case LOCHNAGAR2_SPDIF_MCLK_CTRL:
9962306a36Sopenharmony_ci	case LOCHNAGAR2_GF_CLKOUT1_CTRL...LOCHNAGAR2_GF_CLKOUT2_CTRL:
10062306a36Sopenharmony_ci	case LOCHNAGAR2_ADAT_MCLK_CTRL:
10162306a36Sopenharmony_ci	case LOCHNAGAR2_SOUNDCARD_MCLK_CTRL:
10262306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_FPGA_GPIO1...LOCHNAGAR2_GPIO_FPGA_GPIO6:
10362306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_CDC_GPIO1...LOCHNAGAR2_GPIO_CDC_GPIO8:
10462306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_DSP_GPIO1...LOCHNAGAR2_GPIO_DSP_GPIO6:
10562306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_GF_GPIO2...LOCHNAGAR2_GPIO_GF_GPIO7:
10662306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_CDC_AIF1_BCLK...LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT:
10762306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_DSP_AIF1_BCLK...LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT:
10862306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_PSIA1_BCLK...LOCHNAGAR2_GPIO_PSIA2_TXDAT:
10962306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_GF_AIF3_BCLK...LOCHNAGAR2_GPIO_GF_AIF4_TXDAT:
11062306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_GF_AIF1_BCLK...LOCHNAGAR2_GPIO_GF_AIF2_TXDAT:
11162306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_DSP_UART1_RX...LOCHNAGAR2_GPIO_DSP_UART2_TX:
11262306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_GF_UART2_RX...LOCHNAGAR2_GPIO_GF_UART2_TX:
11362306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_USB_UART_RX:
11462306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_CDC_PDMCLK1...LOCHNAGAR2_GPIO_CDC_PDMDAT2:
11562306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_CDC_DMICCLK1...LOCHNAGAR2_GPIO_CDC_DMICDAT4:
11662306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_DSP_DMICCLK1...LOCHNAGAR2_GPIO_DSP_DMICDAT2:
11762306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_I2C2_SCL...LOCHNAGAR2_GPIO_I2C4_SDA:
11862306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_DSP_STANDBY:
11962306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_CDC_MCLK1...LOCHNAGAR2_GPIO_CDC_MCLK2:
12062306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_DSP_CLKIN:
12162306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_PSIA1_MCLK...LOCHNAGAR2_GPIO_PSIA2_MCLK:
12262306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_GF_GPIO1...LOCHNAGAR2_GPIO_GF_GPIO5:
12362306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_DSP_GPIO20:
12462306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16:
12562306a36Sopenharmony_ci	case LOCHNAGAR2_MINICARD_RESETS:
12662306a36Sopenharmony_ci	case LOCHNAGAR2_ANALOGUE_PATH_CTRL1...LOCHNAGAR2_ANALOGUE_PATH_CTRL2:
12762306a36Sopenharmony_ci	case LOCHNAGAR2_COMMS_CTRL4:
12862306a36Sopenharmony_ci	case LOCHNAGAR2_SPDIF_CTRL:
12962306a36Sopenharmony_ci	case LOCHNAGAR2_IMON_CTRL1...LOCHNAGAR2_IMON_CTRL4:
13062306a36Sopenharmony_ci	case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2:
13162306a36Sopenharmony_ci	case LOCHNAGAR2_POWER_CTRL:
13262306a36Sopenharmony_ci	case LOCHNAGAR2_MICVDD_CTRL1:
13362306a36Sopenharmony_ci	case LOCHNAGAR2_MICVDD_CTRL2:
13462306a36Sopenharmony_ci	case LOCHNAGAR2_VDDCORE_CDC_CTRL1:
13562306a36Sopenharmony_ci	case LOCHNAGAR2_VDDCORE_CDC_CTRL2:
13662306a36Sopenharmony_ci	case LOCHNAGAR2_SOUNDCARD_AIF_CTRL:
13762306a36Sopenharmony_ci		return true;
13862306a36Sopenharmony_ci	default:
13962306a36Sopenharmony_ci		return false;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic bool lochnagar2_volatile_register(struct device *dev, unsigned int reg)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	switch (reg) {
14662306a36Sopenharmony_ci	case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16:
14762306a36Sopenharmony_ci	case LOCHNAGAR2_ANALOGUE_PATH_CTRL1:
14862306a36Sopenharmony_ci	case LOCHNAGAR2_IMON_CTRL3...LOCHNAGAR2_IMON_CTRL4:
14962306a36Sopenharmony_ci	case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2:
15062306a36Sopenharmony_ci		return true;
15162306a36Sopenharmony_ci	default:
15262306a36Sopenharmony_ci		return false;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic const struct regmap_config lochnagar2_i2c_regmap = {
15762306a36Sopenharmony_ci	.reg_bits = 16,
15862306a36Sopenharmony_ci	.val_bits = 16,
15962306a36Sopenharmony_ci	.reg_format_endian = REGMAP_ENDIAN_BIG,
16062306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_BIG,
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	.max_register = 0x1F1F,
16362306a36Sopenharmony_ci	.readable_reg = lochnagar2_readable_register,
16462306a36Sopenharmony_ci	.volatile_reg = lochnagar2_volatile_register,
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic const struct reg_sequence lochnagar2_patch[] = {
17062306a36Sopenharmony_ci	{ 0x00EE, 0x0000 },
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistruct lochnagar_config {
17462306a36Sopenharmony_ci	int id;
17562306a36Sopenharmony_ci	const char * const name;
17662306a36Sopenharmony_ci	enum lochnagar_type type;
17762306a36Sopenharmony_ci	const struct regmap_config *regmap;
17862306a36Sopenharmony_ci	const struct reg_sequence *patch;
17962306a36Sopenharmony_ci	int npatch;
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct lochnagar_config lochnagar_configs[] = {
18362306a36Sopenharmony_ci	{
18462306a36Sopenharmony_ci		.id = 0x50,
18562306a36Sopenharmony_ci		.name = "lochnagar1",
18662306a36Sopenharmony_ci		.type = LOCHNAGAR1,
18762306a36Sopenharmony_ci		.regmap = &lochnagar1_i2c_regmap,
18862306a36Sopenharmony_ci		.patch = lochnagar1_patch,
18962306a36Sopenharmony_ci		.npatch = ARRAY_SIZE(lochnagar1_patch),
19062306a36Sopenharmony_ci	},
19162306a36Sopenharmony_ci	{
19262306a36Sopenharmony_ci		.id = 0xCB58,
19362306a36Sopenharmony_ci		.name = "lochnagar2",
19462306a36Sopenharmony_ci		.type = LOCHNAGAR2,
19562306a36Sopenharmony_ci		.regmap = &lochnagar2_i2c_regmap,
19662306a36Sopenharmony_ci		.patch = lochnagar2_patch,
19762306a36Sopenharmony_ci		.npatch = ARRAY_SIZE(lochnagar2_patch),
19862306a36Sopenharmony_ci	},
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic const struct of_device_id lochnagar_of_match[] = {
20262306a36Sopenharmony_ci	{ .compatible = "cirrus,lochnagar1", .data = &lochnagar_configs[0] },
20362306a36Sopenharmony_ci	{ .compatible = "cirrus,lochnagar2", .data = &lochnagar_configs[1] },
20462306a36Sopenharmony_ci	{},
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int lochnagar_wait_for_boot(struct regmap *regmap, unsigned int *id)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	int i, ret;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	for (i = 0; i < LOCHNAGAR_BOOT_RETRIES; ++i) {
21262306a36Sopenharmony_ci		msleep(LOCHNAGAR_BOOT_DELAY_MS);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		/* The reset register will return the device ID when read */
21562306a36Sopenharmony_ci		ret = regmap_read(regmap, LOCHNAGAR_SOFTWARE_RESET, id);
21662306a36Sopenharmony_ci		if (!ret)
21762306a36Sopenharmony_ci			return ret;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return -ETIMEDOUT;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/**
22462306a36Sopenharmony_ci * lochnagar_update_config - Synchronise the boards analogue configuration to
22562306a36Sopenharmony_ci *                           the hardware.
22662306a36Sopenharmony_ci *
22762306a36Sopenharmony_ci * @lochnagar: A pointer to the primary core data structure.
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * Return: Zero on success or an appropriate negative error code on failure.
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_ciint lochnagar_update_config(struct lochnagar *lochnagar)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct regmap *regmap = lochnagar->regmap;
23462306a36Sopenharmony_ci	unsigned int done = LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK;
23562306a36Sopenharmony_ci	int timeout_ms = LOCHNAGAR_BOOT_DELAY_MS * LOCHNAGAR_BOOT_RETRIES;
23662306a36Sopenharmony_ci	unsigned int val = 0;
23762306a36Sopenharmony_ci	int ret;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	lockdep_assert_held(&lochnagar->analogue_config_lock);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (lochnagar->type != LOCHNAGAR2)
24262306a36Sopenharmony_ci		return 0;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/*
24562306a36Sopenharmony_ci	 * Toggle the ANALOGUE_PATH_UPDATE bit and wait for the device to
24662306a36Sopenharmony_ci	 * acknowledge that any outstanding changes to the analogue
24762306a36Sopenharmony_ci	 * configuration have been applied.
24862306a36Sopenharmony_ci	 */
24962306a36Sopenharmony_ci	ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, 0);
25062306a36Sopenharmony_ci	if (ret < 0)
25162306a36Sopenharmony_ci		return ret;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1,
25462306a36Sopenharmony_ci			   LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK);
25562306a36Sopenharmony_ci	if (ret < 0)
25662306a36Sopenharmony_ci		return ret;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(regmap,
25962306a36Sopenharmony_ci				       LOCHNAGAR2_ANALOGUE_PATH_CTRL1, val,
26062306a36Sopenharmony_ci				       (val & done), LOCHNAGAR_CONFIG_POLL_US,
26162306a36Sopenharmony_ci				       timeout_ms * 1000);
26262306a36Sopenharmony_ci	if (ret < 0)
26362306a36Sopenharmony_ci		return ret;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lochnagar_update_config);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int lochnagar_i2c_probe(struct i2c_client *i2c)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct device *dev = &i2c->dev;
27262306a36Sopenharmony_ci	const struct lochnagar_config *config = NULL;
27362306a36Sopenharmony_ci	const struct of_device_id *of_id;
27462306a36Sopenharmony_ci	struct lochnagar *lochnagar;
27562306a36Sopenharmony_ci	struct gpio_desc *reset, *present;
27662306a36Sopenharmony_ci	unsigned int val;
27762306a36Sopenharmony_ci	unsigned int firmwareid;
27862306a36Sopenharmony_ci	unsigned int devid, rev;
27962306a36Sopenharmony_ci	int ret;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	lochnagar = devm_kzalloc(dev, sizeof(*lochnagar), GFP_KERNEL);
28262306a36Sopenharmony_ci	if (!lochnagar)
28362306a36Sopenharmony_ci		return -ENOMEM;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	of_id = of_match_device(lochnagar_of_match, dev);
28662306a36Sopenharmony_ci	if (!of_id)
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	config = of_id->data;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	lochnagar->dev = dev;
29262306a36Sopenharmony_ci	mutex_init(&lochnagar->analogue_config_lock);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	dev_set_drvdata(dev, lochnagar);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
29762306a36Sopenharmony_ci	if (IS_ERR(reset)) {
29862306a36Sopenharmony_ci		ret = PTR_ERR(reset);
29962306a36Sopenharmony_ci		dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
30062306a36Sopenharmony_ci		return ret;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	present = devm_gpiod_get_optional(dev, "present", GPIOD_OUT_HIGH);
30462306a36Sopenharmony_ci	if (IS_ERR(present)) {
30562306a36Sopenharmony_ci		ret = PTR_ERR(present);
30662306a36Sopenharmony_ci		dev_err(dev, "Failed to get present GPIO: %d\n", ret);
30762306a36Sopenharmony_ci		return ret;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Leave the Lochnagar in reset for a reasonable amount of time */
31162306a36Sopenharmony_ci	msleep(20);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* Bring Lochnagar out of reset */
31462306a36Sopenharmony_ci	gpiod_set_value_cansleep(reset, 1);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Identify Lochnagar */
31762306a36Sopenharmony_ci	lochnagar->type = config->type;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	lochnagar->regmap = devm_regmap_init_i2c(i2c, config->regmap);
32062306a36Sopenharmony_ci	if (IS_ERR(lochnagar->regmap)) {
32162306a36Sopenharmony_ci		ret = PTR_ERR(lochnagar->regmap);
32262306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate register map: %d\n", ret);
32362306a36Sopenharmony_ci		return ret;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* Wait for Lochnagar to boot */
32762306a36Sopenharmony_ci	ret = lochnagar_wait_for_boot(lochnagar->regmap, &val);
32862306a36Sopenharmony_ci	if (ret < 0) {
32962306a36Sopenharmony_ci		dev_err(dev, "Failed to read device ID: %d\n", ret);
33062306a36Sopenharmony_ci		return ret;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	devid = val & LOCHNAGAR_DEVICE_ID_MASK;
33462306a36Sopenharmony_ci	rev = val & LOCHNAGAR_REV_ID_MASK;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (devid != config->id) {
33762306a36Sopenharmony_ci		dev_err(dev,
33862306a36Sopenharmony_ci			"ID does not match %s (expected 0x%x got 0x%x)\n",
33962306a36Sopenharmony_ci			config->name, config->id, devid);
34062306a36Sopenharmony_ci		return -ENODEV;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* Identify firmware */
34462306a36Sopenharmony_ci	ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, &val);
34562306a36Sopenharmony_ci	if (ret < 0) {
34662306a36Sopenharmony_ci		dev_err(dev, "Failed to read firmware id 1: %d\n", ret);
34762306a36Sopenharmony_ci		return ret;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	firmwareid = val;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, &val);
35362306a36Sopenharmony_ci	if (ret < 0) {
35462306a36Sopenharmony_ci		dev_err(dev, "Failed to read firmware id 2: %d\n", ret);
35562306a36Sopenharmony_ci		return ret;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	firmwareid |= (val << config->regmap->val_bits);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	dev_info(dev, "Found %s (0x%x) revision %u firmware 0x%.6x\n",
36162306a36Sopenharmony_ci		 config->name, devid, rev + 1, firmwareid);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = regmap_register_patch(lochnagar->regmap, config->patch,
36462306a36Sopenharmony_ci				    config->npatch);
36562306a36Sopenharmony_ci	if (ret < 0) {
36662306a36Sopenharmony_ci		dev_err(dev, "Failed to register patch: %d\n", ret);
36762306a36Sopenharmony_ci		return ret;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	ret = devm_of_platform_populate(dev);
37162306a36Sopenharmony_ci	if (ret < 0) {
37262306a36Sopenharmony_ci		dev_err(dev, "Failed to populate child nodes: %d\n", ret);
37362306a36Sopenharmony_ci		return ret;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return ret;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic struct i2c_driver lochnagar_i2c_driver = {
38062306a36Sopenharmony_ci	.driver = {
38162306a36Sopenharmony_ci		.name = "lochnagar",
38262306a36Sopenharmony_ci		.of_match_table = lochnagar_of_match,
38362306a36Sopenharmony_ci		.suppress_bind_attrs = true,
38462306a36Sopenharmony_ci	},
38562306a36Sopenharmony_ci	.probe = lochnagar_i2c_probe,
38662306a36Sopenharmony_ci};
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int __init lochnagar_i2c_init(void)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	int ret;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ret = i2c_add_driver(&lochnagar_i2c_driver);
39362306a36Sopenharmony_ci	if (ret)
39462306a36Sopenharmony_ci		pr_err("Failed to register Lochnagar driver: %d\n", ret);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return ret;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_cisubsys_initcall(lochnagar_i2c_init);
399