18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AD5592R Digital <-> Analog converters driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2015-2016 Analog Devices Inc.
68c2ecf20Sopenharmony_ci * Author: Paul Cercueil <paul.cercueil@analog.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "ad5592r-base.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/bitops.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
148c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define AD5592R_GPIO_READBACK_EN	BIT(10)
178c2ecf20Sopenharmony_ci#define AD5592R_LDAC_READBACK_EN	BIT(6)
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
228c2ecf20Sopenharmony_ci	struct spi_transfer t = {
238c2ecf20Sopenharmony_ci			.tx_buf	= &st->spi_msg_nop,
248c2ecf20Sopenharmony_ci			.rx_buf	= buf,
258c2ecf20Sopenharmony_ci			.len = 2
268c2ecf20Sopenharmony_ci		};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	st->spi_msg_nop = 0; /* NOP */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return spi_sync_transfer(spi, &t, 1);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int ad5592r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	st->spi_msg = cpu_to_be16(BIT(15) | (chan << 12) | value);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int ad5592r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
458c2ecf20Sopenharmony_ci	int ret;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	st->spi_msg = cpu_to_be16((AD5592R_REG_ADC_SEQ << 11) | BIT(chan));
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
508c2ecf20Sopenharmony_ci	if (ret)
518c2ecf20Sopenharmony_ci		return ret;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/*
548c2ecf20Sopenharmony_ci	 * Invalid data:
558c2ecf20Sopenharmony_ci	 * See Figure 40. Single-Channel ADC Conversion Sequence
568c2ecf20Sopenharmony_ci	 */
578c2ecf20Sopenharmony_ci	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
588c2ecf20Sopenharmony_ci	if (ret)
598c2ecf20Sopenharmony_ci		return ret;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
628c2ecf20Sopenharmony_ci	if (ret)
638c2ecf20Sopenharmony_ci		return ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	*value = be16_to_cpu(st->spi_msg);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int ad5592r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	st->spi_msg = cpu_to_be16((reg << 11) | value);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int ad5592r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
828c2ecf20Sopenharmony_ci	int ret;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	st->spi_msg = cpu_to_be16((AD5592R_REG_LDAC << 11) |
858c2ecf20Sopenharmony_ci				   AD5592R_LDAC_READBACK_EN | (reg << 2));
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
888c2ecf20Sopenharmony_ci	if (ret)
898c2ecf20Sopenharmony_ci		return ret;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
928c2ecf20Sopenharmony_ci	if (ret)
938c2ecf20Sopenharmony_ci		return ret;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	*value = be16_to_cpu(st->spi_msg);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return 0;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int ad5592r_gpio_read(struct ad5592r_state *st, u8 *value)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	int ret;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	ret = ad5592r_reg_write(st, AD5592R_REG_GPIO_IN_EN,
1058c2ecf20Sopenharmony_ci				AD5592R_GPIO_READBACK_EN | st->gpio_in);
1068c2ecf20Sopenharmony_ci	if (ret)
1078c2ecf20Sopenharmony_ci		return ret;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
1108c2ecf20Sopenharmony_ci	if (ret)
1118c2ecf20Sopenharmony_ci		return ret;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	*value = (u8) be16_to_cpu(st->spi_msg);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic const struct ad5592r_rw_ops ad5592r_rw_ops = {
1198c2ecf20Sopenharmony_ci	.write_dac = ad5592r_write_dac,
1208c2ecf20Sopenharmony_ci	.read_adc = ad5592r_read_adc,
1218c2ecf20Sopenharmony_ci	.reg_write = ad5592r_reg_write,
1228c2ecf20Sopenharmony_ci	.reg_read = ad5592r_reg_read,
1238c2ecf20Sopenharmony_ci	.gpio_read = ad5592r_gpio_read,
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int ad5592r_spi_probe(struct spi_device *spi)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	const struct spi_device_id *id = spi_get_device_id(spi);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return ad5592r_probe(&spi->dev, id->name, &ad5592r_rw_ops);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int ad5592r_spi_remove(struct spi_device *spi)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return ad5592r_remove(&spi->dev);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic const struct spi_device_id ad5592r_spi_ids[] = {
1398c2ecf20Sopenharmony_ci	{ .name = "ad5592r", },
1408c2ecf20Sopenharmony_ci	{}
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ad5592r_spi_ids);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic const struct of_device_id ad5592r_of_match[] = {
1458c2ecf20Sopenharmony_ci	{ .compatible = "adi,ad5592r", },
1468c2ecf20Sopenharmony_ci	{},
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ad5592r_of_match);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic const struct acpi_device_id ad5592r_acpi_match[] = {
1518c2ecf20Sopenharmony_ci	{"ADS5592", },
1528c2ecf20Sopenharmony_ci	{ },
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ad5592r_acpi_match);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic struct spi_driver ad5592r_spi_driver = {
1578c2ecf20Sopenharmony_ci	.driver = {
1588c2ecf20Sopenharmony_ci		.name = "ad5592r",
1598c2ecf20Sopenharmony_ci		.of_match_table = ad5592r_of_match,
1608c2ecf20Sopenharmony_ci		.acpi_match_table = ad5592r_acpi_match,
1618c2ecf20Sopenharmony_ci	},
1628c2ecf20Sopenharmony_ci	.probe = ad5592r_spi_probe,
1638c2ecf20Sopenharmony_ci	.remove = ad5592r_spi_remove,
1648c2ecf20Sopenharmony_ci	.id_table = ad5592r_spi_ids,
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_cimodule_spi_driver(ad5592r_spi_driver);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
1698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
1708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
171