summaryrefslogtreecommitdiff
path: root/drivers/mux/adgs1408.c
blob: 0f7cf54e323481a708078dc67e768f81b5b24f8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * ADGS1408/ADGS1409 SPI MUX driver
 *
 * Copyright 2018 Analog Devices Inc.
 */

#include <linux/err.h>
#include <linux/module.h>
#include <linux/mux/driver.h>
#include <linux/of_platform.h>
#include <linux/property.h>
#include <linux/spi/spi.h>

#define ADGS1408_SW_DATA       (0x01)
#define ADGS1408_REG_READ(reg) ((reg) | 0x80)
#define ADGS1408_DISABLE       (0x00)
#define ADGS1408_MUX(state)    (((state) << 1) | 1)

enum adgs1408_chip_id {
	ADGS1408 = 1,
	ADGS1409,
};

static int adgs1408_spi_reg_write(struct spi_device *spi,
				  u8 reg_addr, u8 reg_data)
{
	u8 tx_buf[2];

	tx_buf[0] = reg_addr;
	tx_buf[1] = reg_data;

	return spi_write_then_read(spi, tx_buf, sizeof(tx_buf), NULL, 0);
}

static int adgs1408_set(struct mux_control *mux, int state)
{
	struct spi_device *spi = to_spi_device(mux->chip->dev.parent);
	u8 reg;

	if (state == MUX_IDLE_DISCONNECT)
		reg = ADGS1408_DISABLE;
	else
		reg = ADGS1408_MUX(state);

	return adgs1408_spi_reg_write(spi, ADGS1408_SW_DATA, reg);
}

static const struct mux_control_ops adgs1408_ops = {
	.set = adgs1408_set,
};

static int adgs1408_probe(struct spi_device *spi)
{
	struct device *dev = &spi->dev;
	enum adgs1408_chip_id chip_id;
	struct mux_chip *mux_chip;
	struct mux_control *mux;
	s32 idle_state;
	int ret;

	chip_id = (enum adgs1408_chip_id)of_device_get_match_data(dev);
	if (!chip_id)
		chip_id = spi_get_device_id(spi)->driver_data;

	mux_chip = devm_mux_chip_alloc(dev, 1, 0);
	if (IS_ERR(mux_chip))
		return PTR_ERR(mux_chip);

	mux_chip->ops = &adgs1408_ops;

	ret = adgs1408_spi_reg_write(spi, ADGS1408_SW_DATA, ADGS1408_DISABLE);
	if (ret < 0)
		return ret;

	ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state);
	if (ret < 0)
		idle_state = MUX_IDLE_AS_IS;

	mux = mux_chip->mux;

	if (chip_id == ADGS1408)
		mux->states = 8;
	else
		mux->states = 4;

	switch (idle_state) {
	case MUX_IDLE_DISCONNECT:
	case MUX_IDLE_AS_IS:
	case 0 ... 7:
		/* adgs1409 supports only 4 states */
		if (idle_state < mux->states) {
			mux->idle_state = idle_state;
			break;
		}
		/* fall through */
	default:
		dev_err(dev, "invalid idle-state %d\n", idle_state);
		return -EINVAL;
	}

	return devm_mux_chip_register(dev, mux_chip);
}

static const struct spi_device_id adgs1408_spi_id[] = {
	{ "adgs1408", ADGS1408 },
	{ "adgs1409", ADGS1409 },
	{ }
};
MODULE_DEVICE_TABLE(spi, adgs1408_spi_id);

static const struct of_device_id adgs1408_of_match[] = {
	{ .compatible = "adi,adgs1408", .data = (void *)ADGS1408, },
	{ .compatible = "adi,adgs1409", .data = (void *)ADGS1409, },
	{ }
};
MODULE_DEVICE_TABLE(of, adgs1408_of_match);

static struct spi_driver adgs1408_driver = {
	.driver = {
		.name = "adgs1408",
		.of_match_table = of_match_ptr(adgs1408_of_match),
	},
	.probe = adgs1408_probe,
	.id_table = adgs1408_spi_id,
};
module_spi_driver(adgs1408_driver);

MODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADGS1408 MUX driver");
MODULE_LICENSE("GPL v2");