18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ADGS1408/ADGS1409 SPI MUX driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2018 Analog Devices Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/mux/driver.h>
128c2ecf20Sopenharmony_ci#include <linux/property.h>
138c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define ADGS1408_SW_DATA       (0x01)
168c2ecf20Sopenharmony_ci#define ADGS1408_REG_READ(reg) ((reg) | 0x80)
178c2ecf20Sopenharmony_ci#define ADGS1408_DISABLE       (0x00)
188c2ecf20Sopenharmony_ci#define ADGS1408_MUX(state)    (((state) << 1) | 1)
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cienum adgs1408_chip_id {
218c2ecf20Sopenharmony_ci	ADGS1408 = 1,
228c2ecf20Sopenharmony_ci	ADGS1409,
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int adgs1408_spi_reg_write(struct spi_device *spi,
268c2ecf20Sopenharmony_ci				  u8 reg_addr, u8 reg_data)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	u8 tx_buf[2];
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	tx_buf[0] = reg_addr;
318c2ecf20Sopenharmony_ci	tx_buf[1] = reg_data;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	return spi_write_then_read(spi, tx_buf, sizeof(tx_buf), NULL, 0);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int adgs1408_set(struct mux_control *mux, int state)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(mux->chip->dev.parent);
398c2ecf20Sopenharmony_ci	u8 reg;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (state == MUX_IDLE_DISCONNECT)
428c2ecf20Sopenharmony_ci		reg = ADGS1408_DISABLE;
438c2ecf20Sopenharmony_ci	else
448c2ecf20Sopenharmony_ci		reg = ADGS1408_MUX(state);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	return adgs1408_spi_reg_write(spi, ADGS1408_SW_DATA, reg);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic const struct mux_control_ops adgs1408_ops = {
508c2ecf20Sopenharmony_ci	.set = adgs1408_set,
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int adgs1408_probe(struct spi_device *spi)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct device *dev = &spi->dev;
568c2ecf20Sopenharmony_ci	enum adgs1408_chip_id chip_id;
578c2ecf20Sopenharmony_ci	struct mux_chip *mux_chip;
588c2ecf20Sopenharmony_ci	struct mux_control *mux;
598c2ecf20Sopenharmony_ci	s32 idle_state;
608c2ecf20Sopenharmony_ci	int ret;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	chip_id = (enum adgs1408_chip_id)device_get_match_data(dev);
638c2ecf20Sopenharmony_ci	if (!chip_id)
648c2ecf20Sopenharmony_ci		chip_id = spi_get_device_id(spi)->driver_data;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	mux_chip = devm_mux_chip_alloc(dev, 1, 0);
678c2ecf20Sopenharmony_ci	if (IS_ERR(mux_chip))
688c2ecf20Sopenharmony_ci		return PTR_ERR(mux_chip);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	mux_chip->ops = &adgs1408_ops;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	ret = adgs1408_spi_reg_write(spi, ADGS1408_SW_DATA, ADGS1408_DISABLE);
738c2ecf20Sopenharmony_ci	if (ret < 0)
748c2ecf20Sopenharmony_ci		return ret;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state);
778c2ecf20Sopenharmony_ci	if (ret < 0)
788c2ecf20Sopenharmony_ci		idle_state = MUX_IDLE_AS_IS;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	mux = mux_chip->mux;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (chip_id == ADGS1408)
838c2ecf20Sopenharmony_ci		mux->states = 8;
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		mux->states = 4;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	switch (idle_state) {
888c2ecf20Sopenharmony_ci	case MUX_IDLE_DISCONNECT:
898c2ecf20Sopenharmony_ci	case MUX_IDLE_AS_IS:
908c2ecf20Sopenharmony_ci	case 0 ... 7:
918c2ecf20Sopenharmony_ci		/* adgs1409 supports only 4 states */
928c2ecf20Sopenharmony_ci		if (idle_state < mux->states) {
938c2ecf20Sopenharmony_ci			mux->idle_state = idle_state;
948c2ecf20Sopenharmony_ci			break;
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci		fallthrough;
978c2ecf20Sopenharmony_ci	default:
988c2ecf20Sopenharmony_ci		dev_err(dev, "invalid idle-state %d\n", idle_state);
998c2ecf20Sopenharmony_ci		return -EINVAL;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return devm_mux_chip_register(dev, mux_chip);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct spi_device_id adgs1408_spi_id[] = {
1068c2ecf20Sopenharmony_ci	{ "adgs1408", ADGS1408 },
1078c2ecf20Sopenharmony_ci	{ "adgs1409", ADGS1409 },
1088c2ecf20Sopenharmony_ci	{ }
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, adgs1408_spi_id);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic const struct of_device_id adgs1408_of_match[] = {
1138c2ecf20Sopenharmony_ci	{ .compatible = "adi,adgs1408", .data = (void *)ADGS1408, },
1148c2ecf20Sopenharmony_ci	{ .compatible = "adi,adgs1409", .data = (void *)ADGS1409, },
1158c2ecf20Sopenharmony_ci	{ }
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, adgs1408_of_match);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic struct spi_driver adgs1408_driver = {
1208c2ecf20Sopenharmony_ci	.driver = {
1218c2ecf20Sopenharmony_ci		.name = "adgs1408",
1228c2ecf20Sopenharmony_ci		.of_match_table = adgs1408_of_match,
1238c2ecf20Sopenharmony_ci	},
1248c2ecf20Sopenharmony_ci	.probe = adgs1408_probe,
1258c2ecf20Sopenharmony_ci	.id_table = adgs1408_spi_id,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_cimodule_spi_driver(adgs1408_driver);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>");
1308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices ADGS1408 MUX driver");
1318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
132