Mircea Caprioru | 8b9ce69 | 2018-08-01 10:37:40 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | /* |
| 3 | * ADGS1408/ADGS1409 SPI MUX driver |
| 4 | * |
| 5 | * Copyright 2018 Analog Devices Inc. |
| 6 | */ |
| 7 | |
| 8 | #include <linux/err.h> |
Andy Shevchenko | b7820ae | 2020-05-29 16:57:26 +0200 | [diff] [blame] | 9 | #include <linux/mod_devicetable.h> |
Mircea Caprioru | 8b9ce69 | 2018-08-01 10:37:40 +0200 | [diff] [blame] | 10 | #include <linux/module.h> |
| 11 | #include <linux/mux/driver.h> |
Mircea Caprioru | 8b9ce69 | 2018-08-01 10:37:40 +0200 | [diff] [blame] | 12 | #include <linux/property.h> |
| 13 | #include <linux/spi/spi.h> |
| 14 | |
| 15 | #define ADGS1408_SW_DATA (0x01) |
| 16 | #define ADGS1408_REG_READ(reg) ((reg) | 0x80) |
| 17 | #define ADGS1408_DISABLE (0x00) |
| 18 | #define ADGS1408_MUX(state) (((state) << 1) | 1) |
| 19 | |
| 20 | enum adgs1408_chip_id { |
| 21 | ADGS1408 = 1, |
| 22 | ADGS1409, |
| 23 | }; |
| 24 | |
| 25 | static int adgs1408_spi_reg_write(struct spi_device *spi, |
| 26 | u8 reg_addr, u8 reg_data) |
| 27 | { |
| 28 | u8 tx_buf[2]; |
| 29 | |
| 30 | tx_buf[0] = reg_addr; |
| 31 | tx_buf[1] = reg_data; |
| 32 | |
| 33 | return spi_write_then_read(spi, tx_buf, sizeof(tx_buf), NULL, 0); |
| 34 | } |
| 35 | |
| 36 | static int adgs1408_set(struct mux_control *mux, int state) |
| 37 | { |
| 38 | struct spi_device *spi = to_spi_device(mux->chip->dev.parent); |
| 39 | u8 reg; |
| 40 | |
| 41 | if (state == MUX_IDLE_DISCONNECT) |
| 42 | reg = ADGS1408_DISABLE; |
| 43 | else |
| 44 | reg = ADGS1408_MUX(state); |
| 45 | |
| 46 | return adgs1408_spi_reg_write(spi, ADGS1408_SW_DATA, reg); |
| 47 | } |
| 48 | |
| 49 | static const struct mux_control_ops adgs1408_ops = { |
| 50 | .set = adgs1408_set, |
| 51 | }; |
| 52 | |
| 53 | static int adgs1408_probe(struct spi_device *spi) |
| 54 | { |
| 55 | struct device *dev = &spi->dev; |
| 56 | enum adgs1408_chip_id chip_id; |
| 57 | struct mux_chip *mux_chip; |
| 58 | struct mux_control *mux; |
| 59 | s32 idle_state; |
| 60 | int ret; |
| 61 | |
Andy Shevchenko | b7820ae | 2020-05-29 16:57:26 +0200 | [diff] [blame] | 62 | chip_id = (enum adgs1408_chip_id)device_get_match_data(dev); |
Mircea Caprioru | 8b9ce69 | 2018-08-01 10:37:40 +0200 | [diff] [blame] | 63 | if (!chip_id) |
| 64 | chip_id = spi_get_device_id(spi)->driver_data; |
| 65 | |
| 66 | mux_chip = devm_mux_chip_alloc(dev, 1, 0); |
| 67 | if (IS_ERR(mux_chip)) |
| 68 | return PTR_ERR(mux_chip); |
| 69 | |
| 70 | mux_chip->ops = &adgs1408_ops; |
| 71 | |
| 72 | ret = adgs1408_spi_reg_write(spi, ADGS1408_SW_DATA, ADGS1408_DISABLE); |
| 73 | if (ret < 0) |
| 74 | return ret; |
| 75 | |
| 76 | ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state); |
| 77 | if (ret < 0) |
| 78 | idle_state = MUX_IDLE_AS_IS; |
| 79 | |
| 80 | mux = mux_chip->mux; |
| 81 | |
| 82 | if (chip_id == ADGS1408) |
| 83 | mux->states = 8; |
| 84 | else |
| 85 | mux->states = 4; |
| 86 | |
| 87 | switch (idle_state) { |
| 88 | case MUX_IDLE_DISCONNECT: |
| 89 | case MUX_IDLE_AS_IS: |
| 90 | case 0 ... 7: |
| 91 | /* adgs1409 supports only 4 states */ |
| 92 | if (idle_state < mux->states) { |
| 93 | mux->idle_state = idle_state; |
| 94 | break; |
| 95 | } |
Gustavo A. R. Silva | df561f66 | 2020-08-23 17:36:59 -0500 | [diff] [blame] | 96 | fallthrough; |
Mircea Caprioru | 8b9ce69 | 2018-08-01 10:37:40 +0200 | [diff] [blame] | 97 | default: |
| 98 | dev_err(dev, "invalid idle-state %d\n", idle_state); |
| 99 | return -EINVAL; |
| 100 | } |
| 101 | |
| 102 | return devm_mux_chip_register(dev, mux_chip); |
| 103 | } |
| 104 | |
| 105 | static const struct spi_device_id adgs1408_spi_id[] = { |
| 106 | { "adgs1408", ADGS1408 }, |
| 107 | { "adgs1409", ADGS1409 }, |
| 108 | { } |
| 109 | }; |
| 110 | MODULE_DEVICE_TABLE(spi, adgs1408_spi_id); |
| 111 | |
| 112 | static const struct of_device_id adgs1408_of_match[] = { |
| 113 | { .compatible = "adi,adgs1408", .data = (void *)ADGS1408, }, |
| 114 | { .compatible = "adi,adgs1409", .data = (void *)ADGS1409, }, |
| 115 | { } |
| 116 | }; |
| 117 | MODULE_DEVICE_TABLE(of, adgs1408_of_match); |
| 118 | |
| 119 | static struct spi_driver adgs1408_driver = { |
| 120 | .driver = { |
| 121 | .name = "adgs1408", |
Andy Shevchenko | b7820ae | 2020-05-29 16:57:26 +0200 | [diff] [blame] | 122 | .of_match_table = adgs1408_of_match, |
Mircea Caprioru | 8b9ce69 | 2018-08-01 10:37:40 +0200 | [diff] [blame] | 123 | }, |
| 124 | .probe = adgs1408_probe, |
| 125 | .id_table = adgs1408_spi_id, |
| 126 | }; |
| 127 | module_spi_driver(adgs1408_driver); |
| 128 | |
| 129 | MODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>"); |
| 130 | MODULE_DESCRIPTION("Analog Devices ADGS1408 MUX driver"); |
Peter Rosin | 38a12607 | 2018-10-12 14:46:40 +0000 | [diff] [blame] | 131 | MODULE_LICENSE("GPL"); |