162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ad525x_dpot: Driver for the Analog Devices digital potentiometers
462306a36Sopenharmony_ci * Copyright (c) 2009-2010 Analog Devices, Inc.
562306a36Sopenharmony_ci * Author: Michael Hennerich <michael.hennerich@analog.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * DEVID		#Wipers		#Positions	Resistor Options (kOhm)
862306a36Sopenharmony_ci * AD5258		1		64		1, 10, 50, 100
962306a36Sopenharmony_ci * AD5259		1		256		5, 10, 50, 100
1062306a36Sopenharmony_ci * AD5251		2		64		1, 10, 50, 100
1162306a36Sopenharmony_ci * AD5252		2		256		1, 10, 50, 100
1262306a36Sopenharmony_ci * AD5255		3		512		25, 250
1362306a36Sopenharmony_ci * AD5253		4		64		1, 10, 50, 100
1462306a36Sopenharmony_ci * AD5254		4		256		1, 10, 50, 100
1562306a36Sopenharmony_ci * AD5160		1		256		5, 10, 50, 100
1662306a36Sopenharmony_ci * AD5161		1		256		5, 10, 50, 100
1762306a36Sopenharmony_ci * AD5162		2		256		2.5, 10, 50, 100
1862306a36Sopenharmony_ci * AD5165		1		256		100
1962306a36Sopenharmony_ci * AD5200		1		256		10, 50
2062306a36Sopenharmony_ci * AD5201		1		33		10, 50
2162306a36Sopenharmony_ci * AD5203		4		64		10, 100
2262306a36Sopenharmony_ci * AD5204		4		256		10, 50, 100
2362306a36Sopenharmony_ci * AD5206		6		256		10, 50, 100
2462306a36Sopenharmony_ci * AD5207		2		256		10, 50, 100
2562306a36Sopenharmony_ci * AD5231		1		1024		10, 50, 100
2662306a36Sopenharmony_ci * AD5232		2		256		10, 50, 100
2762306a36Sopenharmony_ci * AD5233		4		64		10, 50, 100
2862306a36Sopenharmony_ci * AD5235		2		1024		25, 250
2962306a36Sopenharmony_ci * AD5260		1		256		20, 50, 200
3062306a36Sopenharmony_ci * AD5262		2		256		20, 50, 200
3162306a36Sopenharmony_ci * AD5263		4		256		20, 50, 200
3262306a36Sopenharmony_ci * AD5290		1		256		10, 50, 100
3362306a36Sopenharmony_ci * AD5291		1		256		20, 50, 100  (20-TP)
3462306a36Sopenharmony_ci * AD5292		1		1024		20, 50, 100  (20-TP)
3562306a36Sopenharmony_ci * AD5293		1		1024		20, 50, 100
3662306a36Sopenharmony_ci * AD7376		1		128		10, 50, 100, 1M
3762306a36Sopenharmony_ci * AD8400		1		256		1, 10, 50, 100
3862306a36Sopenharmony_ci * AD8402		2		256		1, 10, 50, 100
3962306a36Sopenharmony_ci * AD8403		4		256		1, 10, 50, 100
4062306a36Sopenharmony_ci * ADN2850		3		512		25, 250
4162306a36Sopenharmony_ci * AD5241		1		256		10, 100, 1M
4262306a36Sopenharmony_ci * AD5246		1		128		5, 10, 50, 100
4362306a36Sopenharmony_ci * AD5247		1		128		5, 10, 50, 100
4462306a36Sopenharmony_ci * AD5245		1		256		5, 10, 50, 100
4562306a36Sopenharmony_ci * AD5243		2		256		2.5, 10, 50, 100
4662306a36Sopenharmony_ci * AD5248		2		256		2.5, 10, 50, 100
4762306a36Sopenharmony_ci * AD5242		2		256		20, 50, 200
4862306a36Sopenharmony_ci * AD5280		1		256		20, 50, 200
4962306a36Sopenharmony_ci * AD5282		2		256		20, 50, 200
5062306a36Sopenharmony_ci * ADN2860		3		512		25, 250
5162306a36Sopenharmony_ci * AD5273		1		64		1, 10, 50, 100 (OTP)
5262306a36Sopenharmony_ci * AD5171		1		64		5, 10, 50, 100 (OTP)
5362306a36Sopenharmony_ci * AD5170		1		256		2.5, 10, 50, 100 (OTP)
5462306a36Sopenharmony_ci * AD5172		2		256		2.5, 10, 50, 100 (OTP)
5562306a36Sopenharmony_ci * AD5173		2		256		2.5, 10, 50, 100 (OTP)
5662306a36Sopenharmony_ci * AD5270		1		1024		20, 50, 100 (50-TP)
5762306a36Sopenharmony_ci * AD5271		1		256		20, 50, 100 (50-TP)
5862306a36Sopenharmony_ci * AD5272		1		1024		20, 50, 100 (50-TP)
5962306a36Sopenharmony_ci * AD5274		1		256		20, 50, 100 (50-TP)
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * See Documentation/misc-devices/ad525x_dpot.rst for more info.
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * derived from ad5258.c
6462306a36Sopenharmony_ci * Copyright (c) 2009 Cyber Switching, Inc.
6562306a36Sopenharmony_ci * Author: Chris Verges <chrisv@cyberswitching.com>
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * derived from ad5252.c
6862306a36Sopenharmony_ci * Copyright (c) 2006-2011 Michael Hennerich <michael.hennerich@analog.com>
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#include <linux/module.h>
7262306a36Sopenharmony_ci#include <linux/device.h>
7362306a36Sopenharmony_ci#include <linux/kernel.h>
7462306a36Sopenharmony_ci#include <linux/delay.h>
7562306a36Sopenharmony_ci#include <linux/slab.h>
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#include "ad525x_dpot.h"
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * Client data (each client gets its own)
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct dpot_data {
8462306a36Sopenharmony_ci	struct ad_dpot_bus_data	bdata;
8562306a36Sopenharmony_ci	struct mutex update_lock;
8662306a36Sopenharmony_ci	unsigned int rdac_mask;
8762306a36Sopenharmony_ci	unsigned int max_pos;
8862306a36Sopenharmony_ci	unsigned long devid;
8962306a36Sopenharmony_ci	unsigned int uid;
9062306a36Sopenharmony_ci	unsigned int feat;
9162306a36Sopenharmony_ci	unsigned int wipers;
9262306a36Sopenharmony_ci	u16 rdac_cache[MAX_RDACS];
9362306a36Sopenharmony_ci	DECLARE_BITMAP(otp_en_mask, MAX_RDACS);
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic inline int dpot_read_d8(struct dpot_data *dpot)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	return dpot->bdata.bops->read_d8(dpot->bdata.client);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic inline int dpot_read_r8d8(struct dpot_data *dpot, u8 reg)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	return dpot->bdata.bops->read_r8d8(dpot->bdata.client, reg);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline int dpot_read_r8d16(struct dpot_data *dpot, u8 reg)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	return dpot->bdata.bops->read_r8d16(dpot->bdata.client, reg);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline int dpot_write_d8(struct dpot_data *dpot, u8 val)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	return dpot->bdata.bops->write_d8(dpot->bdata.client, val);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic inline int dpot_write_r8d8(struct dpot_data *dpot, u8 reg, u16 val)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	return dpot->bdata.bops->write_r8d8(dpot->bdata.client, reg, val);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic inline int dpot_write_r8d16(struct dpot_data *dpot, u8 reg, u16 val)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return dpot->bdata.bops->write_r8d16(dpot->bdata.client, reg, val);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic s32 dpot_read_spi(struct dpot_data *dpot, u8 reg)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	unsigned int ctrl = 0;
12962306a36Sopenharmony_ci	int value;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) {
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		if (dpot->feat & F_RDACS_WONLY)
13462306a36Sopenharmony_ci			return dpot->rdac_cache[reg & DPOT_RDAC_MASK];
13562306a36Sopenharmony_ci		if (dpot->uid == DPOT_UID(AD5291_ID) ||
13662306a36Sopenharmony_ci			dpot->uid == DPOT_UID(AD5292_ID) ||
13762306a36Sopenharmony_ci			dpot->uid == DPOT_UID(AD5293_ID)) {
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			value = dpot_read_r8d8(dpot,
14062306a36Sopenharmony_ci				DPOT_AD5291_READ_RDAC << 2);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci			if (value < 0)
14362306a36Sopenharmony_ci				return value;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci			if (dpot->uid == DPOT_UID(AD5291_ID))
14662306a36Sopenharmony_ci				value = value >> 2;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci			return value;
14962306a36Sopenharmony_ci		} else if (dpot->uid == DPOT_UID(AD5270_ID) ||
15062306a36Sopenharmony_ci			dpot->uid == DPOT_UID(AD5271_ID)) {
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci			value = dpot_read_r8d8(dpot,
15362306a36Sopenharmony_ci				DPOT_AD5270_1_2_4_READ_RDAC << 2);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci			if (value < 0)
15662306a36Sopenharmony_ci				return value;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci			if (dpot->uid == DPOT_UID(AD5271_ID))
15962306a36Sopenharmony_ci				value = value >> 2;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci			return value;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		ctrl = DPOT_SPI_READ_RDAC;
16562306a36Sopenharmony_ci	} else if (reg & DPOT_ADDR_EEPROM) {
16662306a36Sopenharmony_ci		ctrl = DPOT_SPI_READ_EEPROM;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (dpot->feat & F_SPI_16BIT)
17062306a36Sopenharmony_ci		return dpot_read_r8d8(dpot, ctrl);
17162306a36Sopenharmony_ci	else if (dpot->feat & F_SPI_24BIT)
17262306a36Sopenharmony_ci		return dpot_read_r8d16(dpot, ctrl);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return -EFAULT;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int value;
18062306a36Sopenharmony_ci	unsigned int ctrl = 0;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	switch (dpot->uid) {
18362306a36Sopenharmony_ci	case DPOT_UID(AD5246_ID):
18462306a36Sopenharmony_ci	case DPOT_UID(AD5247_ID):
18562306a36Sopenharmony_ci		return dpot_read_d8(dpot);
18662306a36Sopenharmony_ci	case DPOT_UID(AD5245_ID):
18762306a36Sopenharmony_ci	case DPOT_UID(AD5241_ID):
18862306a36Sopenharmony_ci	case DPOT_UID(AD5242_ID):
18962306a36Sopenharmony_ci	case DPOT_UID(AD5243_ID):
19062306a36Sopenharmony_ci	case DPOT_UID(AD5248_ID):
19162306a36Sopenharmony_ci	case DPOT_UID(AD5280_ID):
19262306a36Sopenharmony_ci	case DPOT_UID(AD5282_ID):
19362306a36Sopenharmony_ci		ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
19462306a36Sopenharmony_ci			0 : DPOT_AD5282_RDAC_AB;
19562306a36Sopenharmony_ci		return dpot_read_r8d8(dpot, ctrl);
19662306a36Sopenharmony_ci	case DPOT_UID(AD5170_ID):
19762306a36Sopenharmony_ci	case DPOT_UID(AD5171_ID):
19862306a36Sopenharmony_ci	case DPOT_UID(AD5273_ID):
19962306a36Sopenharmony_ci			return dpot_read_d8(dpot);
20062306a36Sopenharmony_ci	case DPOT_UID(AD5172_ID):
20162306a36Sopenharmony_ci	case DPOT_UID(AD5173_ID):
20262306a36Sopenharmony_ci		ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
20362306a36Sopenharmony_ci			0 : DPOT_AD5172_3_A0;
20462306a36Sopenharmony_ci		return dpot_read_r8d8(dpot, ctrl);
20562306a36Sopenharmony_ci	case DPOT_UID(AD5272_ID):
20662306a36Sopenharmony_ci	case DPOT_UID(AD5274_ID):
20762306a36Sopenharmony_ci		dpot_write_r8d8(dpot,
20862306a36Sopenharmony_ci				(DPOT_AD5270_1_2_4_READ_RDAC << 2), 0);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		value = dpot_read_r8d16(dpot, DPOT_AD5270_1_2_4_RDAC << 2);
21162306a36Sopenharmony_ci		if (value < 0)
21262306a36Sopenharmony_ci			return value;
21362306a36Sopenharmony_ci		/*
21462306a36Sopenharmony_ci		 * AD5272/AD5274 returns high byte first, however
21562306a36Sopenharmony_ci		 * underling smbus expects low byte first.
21662306a36Sopenharmony_ci		 */
21762306a36Sopenharmony_ci		value = swab16(value);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if (dpot->uid == DPOT_UID(AD5274_ID))
22062306a36Sopenharmony_ci			value = value >> 2;
22162306a36Sopenharmony_ci		return value;
22262306a36Sopenharmony_ci	default:
22362306a36Sopenharmony_ci		if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256))
22462306a36Sopenharmony_ci			return dpot_read_r8d16(dpot, (reg & 0xF8) |
22562306a36Sopenharmony_ci					((reg & 0x7) << 1));
22662306a36Sopenharmony_ci		else
22762306a36Sopenharmony_ci			return dpot_read_r8d8(dpot, reg);
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic s32 dpot_read(struct dpot_data *dpot, u8 reg)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	if (dpot->feat & F_SPI)
23462306a36Sopenharmony_ci		return dpot_read_spi(dpot, reg);
23562306a36Sopenharmony_ci	else
23662306a36Sopenharmony_ci		return dpot_read_i2c(dpot, reg);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	unsigned int val = 0;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD | DPOT_ADDR_OTP))) {
24462306a36Sopenharmony_ci		if (dpot->feat & F_RDACS_WONLY)
24562306a36Sopenharmony_ci			dpot->rdac_cache[reg & DPOT_RDAC_MASK] = value;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		if (dpot->feat & F_AD_APPDATA) {
24862306a36Sopenharmony_ci			if (dpot->feat & F_SPI_8BIT) {
24962306a36Sopenharmony_ci				val = ((reg & DPOT_RDAC_MASK) <<
25062306a36Sopenharmony_ci					DPOT_MAX_POS(dpot->devid)) |
25162306a36Sopenharmony_ci					value;
25262306a36Sopenharmony_ci				return dpot_write_d8(dpot, val);
25362306a36Sopenharmony_ci			} else if (dpot->feat & F_SPI_16BIT) {
25462306a36Sopenharmony_ci				val = ((reg & DPOT_RDAC_MASK) <<
25562306a36Sopenharmony_ci					DPOT_MAX_POS(dpot->devid)) |
25662306a36Sopenharmony_ci					value;
25762306a36Sopenharmony_ci				return dpot_write_r8d8(dpot, val >> 8,
25862306a36Sopenharmony_ci					val & 0xFF);
25962306a36Sopenharmony_ci			} else
26062306a36Sopenharmony_ci				BUG();
26162306a36Sopenharmony_ci		} else {
26262306a36Sopenharmony_ci			if (dpot->uid == DPOT_UID(AD5291_ID) ||
26362306a36Sopenharmony_ci				dpot->uid == DPOT_UID(AD5292_ID) ||
26462306a36Sopenharmony_ci				dpot->uid == DPOT_UID(AD5293_ID)) {
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci				dpot_write_r8d8(dpot, DPOT_AD5291_CTRLREG << 2,
26762306a36Sopenharmony_ci						DPOT_AD5291_UNLOCK_CMD);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci				if (dpot->uid == DPOT_UID(AD5291_ID))
27062306a36Sopenharmony_ci					value = value << 2;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci				return dpot_write_r8d8(dpot,
27362306a36Sopenharmony_ci					(DPOT_AD5291_RDAC << 2) |
27462306a36Sopenharmony_ci					(value >> 8), value & 0xFF);
27562306a36Sopenharmony_ci			} else if (dpot->uid == DPOT_UID(AD5270_ID) ||
27662306a36Sopenharmony_ci				dpot->uid == DPOT_UID(AD5271_ID)) {
27762306a36Sopenharmony_ci				dpot_write_r8d8(dpot,
27862306a36Sopenharmony_ci						DPOT_AD5270_1_2_4_CTRLREG << 2,
27962306a36Sopenharmony_ci						DPOT_AD5270_1_2_4_UNLOCK_CMD);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci				if (dpot->uid == DPOT_UID(AD5271_ID))
28262306a36Sopenharmony_ci					value = value << 2;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci				return dpot_write_r8d8(dpot,
28562306a36Sopenharmony_ci					(DPOT_AD5270_1_2_4_RDAC << 2) |
28662306a36Sopenharmony_ci					(value >> 8), value & 0xFF);
28762306a36Sopenharmony_ci			}
28862306a36Sopenharmony_ci			val = DPOT_SPI_RDAC | (reg & DPOT_RDAC_MASK);
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci	} else if (reg & DPOT_ADDR_EEPROM) {
29162306a36Sopenharmony_ci		val = DPOT_SPI_EEPROM | (reg & DPOT_RDAC_MASK);
29262306a36Sopenharmony_ci	} else if (reg & DPOT_ADDR_CMD) {
29362306a36Sopenharmony_ci		switch (reg) {
29462306a36Sopenharmony_ci		case DPOT_DEC_ALL_6DB:
29562306a36Sopenharmony_ci			val = DPOT_SPI_DEC_ALL_6DB;
29662306a36Sopenharmony_ci			break;
29762306a36Sopenharmony_ci		case DPOT_INC_ALL_6DB:
29862306a36Sopenharmony_ci			val = DPOT_SPI_INC_ALL_6DB;
29962306a36Sopenharmony_ci			break;
30062306a36Sopenharmony_ci		case DPOT_DEC_ALL:
30162306a36Sopenharmony_ci			val = DPOT_SPI_DEC_ALL;
30262306a36Sopenharmony_ci			break;
30362306a36Sopenharmony_ci		case DPOT_INC_ALL:
30462306a36Sopenharmony_ci			val = DPOT_SPI_INC_ALL;
30562306a36Sopenharmony_ci			break;
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci	} else if (reg & DPOT_ADDR_OTP) {
30862306a36Sopenharmony_ci		if (dpot->uid == DPOT_UID(AD5291_ID) ||
30962306a36Sopenharmony_ci			dpot->uid == DPOT_UID(AD5292_ID)) {
31062306a36Sopenharmony_ci			return dpot_write_r8d8(dpot,
31162306a36Sopenharmony_ci				DPOT_AD5291_STORE_XTPM << 2, 0);
31262306a36Sopenharmony_ci		} else if (dpot->uid == DPOT_UID(AD5270_ID) ||
31362306a36Sopenharmony_ci			dpot->uid == DPOT_UID(AD5271_ID)) {
31462306a36Sopenharmony_ci			return dpot_write_r8d8(dpot,
31562306a36Sopenharmony_ci				DPOT_AD5270_1_2_4_STORE_XTPM << 2, 0);
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci	} else
31862306a36Sopenharmony_ci		BUG();
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (dpot->feat & F_SPI_16BIT)
32162306a36Sopenharmony_ci		return dpot_write_r8d8(dpot, val, value);
32262306a36Sopenharmony_ci	else if (dpot->feat & F_SPI_24BIT)
32362306a36Sopenharmony_ci		return dpot_write_r8d16(dpot, val, value);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return -EFAULT;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	/* Only write the instruction byte for certain commands */
33162306a36Sopenharmony_ci	unsigned int tmp = 0, ctrl = 0;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	switch (dpot->uid) {
33462306a36Sopenharmony_ci	case DPOT_UID(AD5246_ID):
33562306a36Sopenharmony_ci	case DPOT_UID(AD5247_ID):
33662306a36Sopenharmony_ci		return dpot_write_d8(dpot, value);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	case DPOT_UID(AD5245_ID):
33962306a36Sopenharmony_ci	case DPOT_UID(AD5241_ID):
34062306a36Sopenharmony_ci	case DPOT_UID(AD5242_ID):
34162306a36Sopenharmony_ci	case DPOT_UID(AD5243_ID):
34262306a36Sopenharmony_ci	case DPOT_UID(AD5248_ID):
34362306a36Sopenharmony_ci	case DPOT_UID(AD5280_ID):
34462306a36Sopenharmony_ci	case DPOT_UID(AD5282_ID):
34562306a36Sopenharmony_ci		ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
34662306a36Sopenharmony_ci			0 : DPOT_AD5282_RDAC_AB;
34762306a36Sopenharmony_ci		return dpot_write_r8d8(dpot, ctrl, value);
34862306a36Sopenharmony_ci	case DPOT_UID(AD5171_ID):
34962306a36Sopenharmony_ci	case DPOT_UID(AD5273_ID):
35062306a36Sopenharmony_ci		if (reg & DPOT_ADDR_OTP) {
35162306a36Sopenharmony_ci			tmp = dpot_read_d8(dpot);
35262306a36Sopenharmony_ci			if (tmp >> 6) /* Ready to Program? */
35362306a36Sopenharmony_ci				return -EFAULT;
35462306a36Sopenharmony_ci			ctrl = DPOT_AD5273_FUSE;
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci		return dpot_write_r8d8(dpot, ctrl, value);
35762306a36Sopenharmony_ci	case DPOT_UID(AD5172_ID):
35862306a36Sopenharmony_ci	case DPOT_UID(AD5173_ID):
35962306a36Sopenharmony_ci		ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
36062306a36Sopenharmony_ci			0 : DPOT_AD5172_3_A0;
36162306a36Sopenharmony_ci		if (reg & DPOT_ADDR_OTP) {
36262306a36Sopenharmony_ci			tmp = dpot_read_r8d16(dpot, ctrl);
36362306a36Sopenharmony_ci			if (tmp >> 14) /* Ready to Program? */
36462306a36Sopenharmony_ci				return -EFAULT;
36562306a36Sopenharmony_ci			ctrl |= DPOT_AD5170_2_3_FUSE;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci		return dpot_write_r8d8(dpot, ctrl, value);
36862306a36Sopenharmony_ci	case DPOT_UID(AD5170_ID):
36962306a36Sopenharmony_ci		if (reg & DPOT_ADDR_OTP) {
37062306a36Sopenharmony_ci			tmp = dpot_read_r8d16(dpot, tmp);
37162306a36Sopenharmony_ci			if (tmp >> 14) /* Ready to Program? */
37262306a36Sopenharmony_ci				return -EFAULT;
37362306a36Sopenharmony_ci			ctrl = DPOT_AD5170_2_3_FUSE;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci		return dpot_write_r8d8(dpot, ctrl, value);
37662306a36Sopenharmony_ci	case DPOT_UID(AD5272_ID):
37762306a36Sopenharmony_ci	case DPOT_UID(AD5274_ID):
37862306a36Sopenharmony_ci		dpot_write_r8d8(dpot, DPOT_AD5270_1_2_4_CTRLREG << 2,
37962306a36Sopenharmony_ci				DPOT_AD5270_1_2_4_UNLOCK_CMD);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		if (reg & DPOT_ADDR_OTP)
38262306a36Sopenharmony_ci			return dpot_write_r8d8(dpot,
38362306a36Sopenharmony_ci					DPOT_AD5270_1_2_4_STORE_XTPM << 2, 0);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (dpot->uid == DPOT_UID(AD5274_ID))
38662306a36Sopenharmony_ci			value = value << 2;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		return dpot_write_r8d8(dpot, (DPOT_AD5270_1_2_4_RDAC << 2) |
38962306a36Sopenharmony_ci				       (value >> 8), value & 0xFF);
39062306a36Sopenharmony_ci	default:
39162306a36Sopenharmony_ci		if (reg & DPOT_ADDR_CMD)
39262306a36Sopenharmony_ci			return dpot_write_d8(dpot, reg);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		if (dpot->max_pos > 256)
39562306a36Sopenharmony_ci			return dpot_write_r8d16(dpot, (reg & 0xF8) |
39662306a36Sopenharmony_ci						((reg & 0x7) << 1), value);
39762306a36Sopenharmony_ci		else
39862306a36Sopenharmony_ci			/* All other registers require instruction + data bytes */
39962306a36Sopenharmony_ci			return dpot_write_r8d8(dpot, reg, value);
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic s32 dpot_write(struct dpot_data *dpot, u8 reg, u16 value)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	if (dpot->feat & F_SPI)
40662306a36Sopenharmony_ci		return dpot_write_spi(dpot, reg, value);
40762306a36Sopenharmony_ci	else
40862306a36Sopenharmony_ci		return dpot_write_i2c(dpot, reg, value);
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/* sysfs functions */
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic ssize_t sysfs_show_reg(struct device *dev,
41462306a36Sopenharmony_ci			      struct device_attribute *attr,
41562306a36Sopenharmony_ci			      char *buf, u32 reg)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct dpot_data *data = dev_get_drvdata(dev);
41862306a36Sopenharmony_ci	s32 value;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (reg & DPOT_ADDR_OTP_EN)
42162306a36Sopenharmony_ci		return sprintf(buf, "%s\n",
42262306a36Sopenharmony_ci			test_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask) ?
42362306a36Sopenharmony_ci			"enabled" : "disabled");
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
42762306a36Sopenharmony_ci	value = dpot_read(data, reg);
42862306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (value < 0)
43162306a36Sopenharmony_ci		return -EINVAL;
43262306a36Sopenharmony_ci	/*
43362306a36Sopenharmony_ci	 * Let someone else deal with converting this ...
43462306a36Sopenharmony_ci	 * the tolerance is a two-byte value where the MSB
43562306a36Sopenharmony_ci	 * is a sign + integer value, and the LSB is a
43662306a36Sopenharmony_ci	 * decimal value.  See page 18 of the AD5258
43762306a36Sopenharmony_ci	 * datasheet (Rev. A) for more details.
43862306a36Sopenharmony_ci	 */
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (reg & DPOT_REG_TOL)
44162306a36Sopenharmony_ci		return sprintf(buf, "0x%04x\n", value & 0xFFFF);
44262306a36Sopenharmony_ci	else
44362306a36Sopenharmony_ci		return sprintf(buf, "%u\n", value & data->rdac_mask);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic ssize_t sysfs_set_reg(struct device *dev,
44762306a36Sopenharmony_ci			     struct device_attribute *attr,
44862306a36Sopenharmony_ci			     const char *buf, size_t count, u32 reg)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct dpot_data *data = dev_get_drvdata(dev);
45162306a36Sopenharmony_ci	unsigned long value;
45262306a36Sopenharmony_ci	int err;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (reg & DPOT_ADDR_OTP_EN) {
45562306a36Sopenharmony_ci		if (sysfs_streq(buf, "enabled"))
45662306a36Sopenharmony_ci			set_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask);
45762306a36Sopenharmony_ci		else
45862306a36Sopenharmony_ci			clear_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		return count;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if ((reg & DPOT_ADDR_OTP) &&
46462306a36Sopenharmony_ci		!test_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask))
46562306a36Sopenharmony_ci		return -EPERM;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	err = kstrtoul(buf, 10, &value);
46862306a36Sopenharmony_ci	if (err)
46962306a36Sopenharmony_ci		return err;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (value > data->rdac_mask)
47262306a36Sopenharmony_ci		value = data->rdac_mask;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
47562306a36Sopenharmony_ci	dpot_write(data, reg, value);
47662306a36Sopenharmony_ci	if (reg & DPOT_ADDR_EEPROM)
47762306a36Sopenharmony_ci		msleep(26);	/* Sleep while the EEPROM updates */
47862306a36Sopenharmony_ci	else if (reg & DPOT_ADDR_OTP)
47962306a36Sopenharmony_ci		msleep(400);	/* Sleep while the OTP updates */
48062306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return count;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic ssize_t sysfs_do_cmd(struct device *dev,
48662306a36Sopenharmony_ci			    struct device_attribute *attr,
48762306a36Sopenharmony_ci			    const char *buf, size_t count, u32 reg)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct dpot_data *data = dev_get_drvdata(dev);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
49262306a36Sopenharmony_ci	dpot_write(data, reg, 0);
49362306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return count;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci#define DPOT_DEVICE_SHOW(_name, _reg) static ssize_t \
50162306a36Sopenharmony_cishow_##_name(struct device *dev, \
50262306a36Sopenharmony_ci			  struct device_attribute *attr, char *buf) \
50362306a36Sopenharmony_ci{ \
50462306a36Sopenharmony_ci	return sysfs_show_reg(dev, attr, buf, _reg); \
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci#define DPOT_DEVICE_SET(_name, _reg) static ssize_t \
50862306a36Sopenharmony_ciset_##_name(struct device *dev, \
50962306a36Sopenharmony_ci			 struct device_attribute *attr, \
51062306a36Sopenharmony_ci			 const char *buf, size_t count) \
51162306a36Sopenharmony_ci{ \
51262306a36Sopenharmony_ci	return sysfs_set_reg(dev, attr, buf, count, _reg); \
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci#define DPOT_DEVICE_SHOW_SET(name, reg) \
51662306a36Sopenharmony_ciDPOT_DEVICE_SHOW(name, reg) \
51762306a36Sopenharmony_ciDPOT_DEVICE_SET(name, reg) \
51862306a36Sopenharmony_cistatic DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, set_##name)
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci#define DPOT_DEVICE_SHOW_ONLY(name, reg) \
52162306a36Sopenharmony_ciDPOT_DEVICE_SHOW(name, reg) \
52262306a36Sopenharmony_cistatic DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, NULL)
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(rdac0, DPOT_ADDR_RDAC | DPOT_RDAC0);
52562306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(eeprom0, DPOT_ADDR_EEPROM | DPOT_RDAC0);
52662306a36Sopenharmony_ciDPOT_DEVICE_SHOW_ONLY(tolerance0, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC0);
52762306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp0, DPOT_ADDR_OTP | DPOT_RDAC0);
52862306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp0en, DPOT_ADDR_OTP_EN | DPOT_RDAC0);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(rdac1, DPOT_ADDR_RDAC | DPOT_RDAC1);
53162306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(eeprom1, DPOT_ADDR_EEPROM | DPOT_RDAC1);
53262306a36Sopenharmony_ciDPOT_DEVICE_SHOW_ONLY(tolerance1, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC1);
53362306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp1, DPOT_ADDR_OTP | DPOT_RDAC1);
53462306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp1en, DPOT_ADDR_OTP_EN | DPOT_RDAC1);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(rdac2, DPOT_ADDR_RDAC | DPOT_RDAC2);
53762306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(eeprom2, DPOT_ADDR_EEPROM | DPOT_RDAC2);
53862306a36Sopenharmony_ciDPOT_DEVICE_SHOW_ONLY(tolerance2, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC2);
53962306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp2, DPOT_ADDR_OTP | DPOT_RDAC2);
54062306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp2en, DPOT_ADDR_OTP_EN | DPOT_RDAC2);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(rdac3, DPOT_ADDR_RDAC | DPOT_RDAC3);
54362306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(eeprom3, DPOT_ADDR_EEPROM | DPOT_RDAC3);
54462306a36Sopenharmony_ciDPOT_DEVICE_SHOW_ONLY(tolerance3, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC3);
54562306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp3, DPOT_ADDR_OTP | DPOT_RDAC3);
54662306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp3en, DPOT_ADDR_OTP_EN | DPOT_RDAC3);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(rdac4, DPOT_ADDR_RDAC | DPOT_RDAC4);
54962306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(eeprom4, DPOT_ADDR_EEPROM | DPOT_RDAC4);
55062306a36Sopenharmony_ciDPOT_DEVICE_SHOW_ONLY(tolerance4, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC4);
55162306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp4, DPOT_ADDR_OTP | DPOT_RDAC4);
55262306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp4en, DPOT_ADDR_OTP_EN | DPOT_RDAC4);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(rdac5, DPOT_ADDR_RDAC | DPOT_RDAC5);
55562306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(eeprom5, DPOT_ADDR_EEPROM | DPOT_RDAC5);
55662306a36Sopenharmony_ciDPOT_DEVICE_SHOW_ONLY(tolerance5, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC5);
55762306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp5, DPOT_ADDR_OTP | DPOT_RDAC5);
55862306a36Sopenharmony_ciDPOT_DEVICE_SHOW_SET(otp5en, DPOT_ADDR_OTP_EN | DPOT_RDAC5);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic const struct attribute *dpot_attrib_wipers[] = {
56162306a36Sopenharmony_ci	&dev_attr_rdac0.attr,
56262306a36Sopenharmony_ci	&dev_attr_rdac1.attr,
56362306a36Sopenharmony_ci	&dev_attr_rdac2.attr,
56462306a36Sopenharmony_ci	&dev_attr_rdac3.attr,
56562306a36Sopenharmony_ci	&dev_attr_rdac4.attr,
56662306a36Sopenharmony_ci	&dev_attr_rdac5.attr,
56762306a36Sopenharmony_ci	NULL
56862306a36Sopenharmony_ci};
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic const struct attribute *dpot_attrib_eeprom[] = {
57162306a36Sopenharmony_ci	&dev_attr_eeprom0.attr,
57262306a36Sopenharmony_ci	&dev_attr_eeprom1.attr,
57362306a36Sopenharmony_ci	&dev_attr_eeprom2.attr,
57462306a36Sopenharmony_ci	&dev_attr_eeprom3.attr,
57562306a36Sopenharmony_ci	&dev_attr_eeprom4.attr,
57662306a36Sopenharmony_ci	&dev_attr_eeprom5.attr,
57762306a36Sopenharmony_ci	NULL
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic const struct attribute *dpot_attrib_otp[] = {
58162306a36Sopenharmony_ci	&dev_attr_otp0.attr,
58262306a36Sopenharmony_ci	&dev_attr_otp1.attr,
58362306a36Sopenharmony_ci	&dev_attr_otp2.attr,
58462306a36Sopenharmony_ci	&dev_attr_otp3.attr,
58562306a36Sopenharmony_ci	&dev_attr_otp4.attr,
58662306a36Sopenharmony_ci	&dev_attr_otp5.attr,
58762306a36Sopenharmony_ci	NULL
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic const struct attribute *dpot_attrib_otp_en[] = {
59162306a36Sopenharmony_ci	&dev_attr_otp0en.attr,
59262306a36Sopenharmony_ci	&dev_attr_otp1en.attr,
59362306a36Sopenharmony_ci	&dev_attr_otp2en.attr,
59462306a36Sopenharmony_ci	&dev_attr_otp3en.attr,
59562306a36Sopenharmony_ci	&dev_attr_otp4en.attr,
59662306a36Sopenharmony_ci	&dev_attr_otp5en.attr,
59762306a36Sopenharmony_ci	NULL
59862306a36Sopenharmony_ci};
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic const struct attribute *dpot_attrib_tolerance[] = {
60162306a36Sopenharmony_ci	&dev_attr_tolerance0.attr,
60262306a36Sopenharmony_ci	&dev_attr_tolerance1.attr,
60362306a36Sopenharmony_ci	&dev_attr_tolerance2.attr,
60462306a36Sopenharmony_ci	&dev_attr_tolerance3.attr,
60562306a36Sopenharmony_ci	&dev_attr_tolerance4.attr,
60662306a36Sopenharmony_ci	&dev_attr_tolerance5.attr,
60762306a36Sopenharmony_ci	NULL
60862306a36Sopenharmony_ci};
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci#define DPOT_DEVICE_DO_CMD(_name, _cmd) static ssize_t \
61362306a36Sopenharmony_ciset_##_name(struct device *dev, \
61462306a36Sopenharmony_ci			 struct device_attribute *attr, \
61562306a36Sopenharmony_ci			 const char *buf, size_t count) \
61662306a36Sopenharmony_ci{ \
61762306a36Sopenharmony_ci	return sysfs_do_cmd(dev, attr, buf, count, _cmd); \
61862306a36Sopenharmony_ci} \
61962306a36Sopenharmony_cistatic DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, NULL, set_##_name)
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciDPOT_DEVICE_DO_CMD(inc_all, DPOT_INC_ALL);
62262306a36Sopenharmony_ciDPOT_DEVICE_DO_CMD(dec_all, DPOT_DEC_ALL);
62362306a36Sopenharmony_ciDPOT_DEVICE_DO_CMD(inc_all_6db, DPOT_INC_ALL_6DB);
62462306a36Sopenharmony_ciDPOT_DEVICE_DO_CMD(dec_all_6db, DPOT_DEC_ALL_6DB);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic struct attribute *ad525x_attributes_commands[] = {
62762306a36Sopenharmony_ci	&dev_attr_inc_all.attr,
62862306a36Sopenharmony_ci	&dev_attr_dec_all.attr,
62962306a36Sopenharmony_ci	&dev_attr_inc_all_6db.attr,
63062306a36Sopenharmony_ci	&dev_attr_dec_all_6db.attr,
63162306a36Sopenharmony_ci	NULL
63262306a36Sopenharmony_ci};
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic const struct attribute_group ad525x_group_commands = {
63562306a36Sopenharmony_ci	.attrs = ad525x_attributes_commands,
63662306a36Sopenharmony_ci};
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic int ad_dpot_add_files(struct device *dev,
63962306a36Sopenharmony_ci		unsigned int features, unsigned int rdac)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	int err = sysfs_create_file(&dev->kobj,
64262306a36Sopenharmony_ci		dpot_attrib_wipers[rdac]);
64362306a36Sopenharmony_ci	if (features & F_CMD_EEP)
64462306a36Sopenharmony_ci		err |= sysfs_create_file(&dev->kobj,
64562306a36Sopenharmony_ci			dpot_attrib_eeprom[rdac]);
64662306a36Sopenharmony_ci	if (features & F_CMD_TOL)
64762306a36Sopenharmony_ci		err |= sysfs_create_file(&dev->kobj,
64862306a36Sopenharmony_ci			dpot_attrib_tolerance[rdac]);
64962306a36Sopenharmony_ci	if (features & F_CMD_OTP) {
65062306a36Sopenharmony_ci		err |= sysfs_create_file(&dev->kobj,
65162306a36Sopenharmony_ci			dpot_attrib_otp_en[rdac]);
65262306a36Sopenharmony_ci		err |= sysfs_create_file(&dev->kobj,
65362306a36Sopenharmony_ci			dpot_attrib_otp[rdac]);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (err)
65762306a36Sopenharmony_ci		dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
65862306a36Sopenharmony_ci			rdac);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return err;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic inline void ad_dpot_remove_files(struct device *dev,
66462306a36Sopenharmony_ci		unsigned int features, unsigned int rdac)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	sysfs_remove_file(&dev->kobj,
66762306a36Sopenharmony_ci		dpot_attrib_wipers[rdac]);
66862306a36Sopenharmony_ci	if (features & F_CMD_EEP)
66962306a36Sopenharmony_ci		sysfs_remove_file(&dev->kobj,
67062306a36Sopenharmony_ci			dpot_attrib_eeprom[rdac]);
67162306a36Sopenharmony_ci	if (features & F_CMD_TOL)
67262306a36Sopenharmony_ci		sysfs_remove_file(&dev->kobj,
67362306a36Sopenharmony_ci			dpot_attrib_tolerance[rdac]);
67462306a36Sopenharmony_ci	if (features & F_CMD_OTP) {
67562306a36Sopenharmony_ci		sysfs_remove_file(&dev->kobj,
67662306a36Sopenharmony_ci			dpot_attrib_otp_en[rdac]);
67762306a36Sopenharmony_ci		sysfs_remove_file(&dev->kobj,
67862306a36Sopenharmony_ci			dpot_attrib_otp[rdac]);
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ciint ad_dpot_probe(struct device *dev,
68362306a36Sopenharmony_ci		struct ad_dpot_bus_data *bdata, unsigned long devid,
68462306a36Sopenharmony_ci			    const char *name)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	struct dpot_data *data;
68862306a36Sopenharmony_ci	int i, err = 0;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL);
69162306a36Sopenharmony_ci	if (!data) {
69262306a36Sopenharmony_ci		err = -ENOMEM;
69362306a36Sopenharmony_ci		goto exit;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	dev_set_drvdata(dev, data);
69762306a36Sopenharmony_ci	mutex_init(&data->update_lock);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	data->bdata = *bdata;
70062306a36Sopenharmony_ci	data->devid = devid;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	data->max_pos = 1 << DPOT_MAX_POS(devid);
70362306a36Sopenharmony_ci	data->rdac_mask = data->max_pos - 1;
70462306a36Sopenharmony_ci	data->feat = DPOT_FEAT(devid);
70562306a36Sopenharmony_ci	data->uid = DPOT_UID(devid);
70662306a36Sopenharmony_ci	data->wipers = DPOT_WIPERS(devid);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
70962306a36Sopenharmony_ci		if (data->wipers & (1 << i)) {
71062306a36Sopenharmony_ci			err = ad_dpot_add_files(dev, data->feat, i);
71162306a36Sopenharmony_ci			if (err)
71262306a36Sopenharmony_ci				goto exit_remove_files;
71362306a36Sopenharmony_ci			/* power-up midscale */
71462306a36Sopenharmony_ci			if (data->feat & F_RDACS_WONLY)
71562306a36Sopenharmony_ci				data->rdac_cache[i] = data->max_pos / 2;
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (data->feat & F_CMD_INC)
71962306a36Sopenharmony_ci		err = sysfs_create_group(&dev->kobj, &ad525x_group_commands);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	if (err) {
72262306a36Sopenharmony_ci		dev_err(dev, "failed to register sysfs hooks\n");
72362306a36Sopenharmony_ci		goto exit_free;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
72762306a36Sopenharmony_ci		 name, data->max_pos);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ciexit_remove_files:
73262306a36Sopenharmony_ci	for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
73362306a36Sopenharmony_ci		if (data->wipers & (1 << i))
73462306a36Sopenharmony_ci			ad_dpot_remove_files(dev, data->feat, i);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ciexit_free:
73762306a36Sopenharmony_ci	kfree(data);
73862306a36Sopenharmony_ci	dev_set_drvdata(dev, NULL);
73962306a36Sopenharmony_ciexit:
74062306a36Sopenharmony_ci	dev_err(dev, "failed to create client for %s ID 0x%lX\n",
74162306a36Sopenharmony_ci		name, devid);
74262306a36Sopenharmony_ci	return err;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ciEXPORT_SYMBOL(ad_dpot_probe);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_civoid ad_dpot_remove(struct device *dev)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct dpot_data *data = dev_get_drvdata(dev);
74962306a36Sopenharmony_ci	int i;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
75262306a36Sopenharmony_ci		if (data->wipers & (1 << i))
75362306a36Sopenharmony_ci			ad_dpot_remove_files(dev, data->feat, i);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	kfree(data);
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ciEXPORT_SYMBOL(ad_dpot_remove);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ciMODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, "
76162306a36Sopenharmony_ci	      "Michael Hennerich <michael.hennerich@analog.com>");
76262306a36Sopenharmony_ciMODULE_DESCRIPTION("Digital potentiometer driver");
76362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
764