// SPDX-License-Identifier: GPL-2.0 /* * * x9250.c -- Renesas X9250 potentiometers IIO driver * * Copyright 2023 CS GROUP France * * Author: Herve Codina */ #include #include #include #include #include #include #include #include struct x9250_cfg { const char *name; int kohms; }; struct x9250 { struct spi_device *spi; const struct x9250_cfg *cfg; struct gpio_desc *wp_gpio; }; #define X9250_ID 0x50 #define X9250_CMD_RD_WCR(_p) (0x90 | (_p)) #define X9250_CMD_WR_WCR(_p) (0xa0 | (_p)) static int x9250_write8(struct x9250 *x9250, u8 cmd, u8 val) { u8 txbuf[3]; txbuf[0] = X9250_ID; txbuf[1] = cmd; txbuf[2] = val; return spi_write_then_read(x9250->spi, txbuf, ARRAY_SIZE(txbuf), NULL, 0); } static int x9250_read8(struct x9250 *x9250, u8 cmd, u8 *val) { u8 txbuf[2]; txbuf[0] = X9250_ID; txbuf[1] = cmd; return spi_write_then_read(x9250->spi, txbuf, ARRAY_SIZE(txbuf), val, 1); } #define X9250_CHANNEL(ch) { \ .type = IIO_RESISTANCE, \ .indexed = 1, \ .output = 1, \ .channel = (ch), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \ } static const struct iio_chan_spec x9250_channels[] = { X9250_CHANNEL(0), X9250_CHANNEL(1), X9250_CHANNEL(2), X9250_CHANNEL(3), }; static int x9250_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct x9250 *x9250 = iio_priv(indio_dev); int ch = chan->channel; int ret; u8 v; switch (mask) { case IIO_CHAN_INFO_RAW: ret = x9250_read8(x9250, X9250_CMD_RD_WCR(ch), &v); if (ret) return ret; *val = v; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 1000 * x9250->cfg->kohms; *val2 = U8_MAX; return IIO_VAL_FRACTIONAL; } return -EINVAL; } static int x9250_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { static const int range[] = {0, 1, 255}; /* min, step, max */ switch (mask) { case IIO_CHAN_INFO_RAW: *length = ARRAY_SIZE(range); *vals = range; *type = IIO_VAL_INT; return IIO_AVAIL_RANGE; } return -EINVAL; } static int x9250_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct x9250 *x9250 = iio_priv(indio_dev); int ch = chan->channel; int ret; if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; if (val > U8_MAX || val < 0) return -EINVAL; gpiod_set_value_cansleep(x9250->wp_gpio, 0); ret = x9250_write8(x9250, X9250_CMD_WR_WCR(ch), val); gpiod_set_value_cansleep(x9250->wp_gpio, 1); return ret; } static const struct iio_info x9250_info = { .read_raw = x9250_read_raw, .read_avail = x9250_read_avail, .write_raw = x9250_write_raw, }; enum x9250_type { X9250T, X9250U, }; static const struct x9250_cfg x9250_cfg[] = { [X9250T] = { .name = "x9250t", .kohms = 100, }, [X9250U] = { .name = "x9250u", .kohms = 50, }, }; static const char *const x9250_regulator_names[] = { "vcc", "avp", "avn", }; static int x9250_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct x9250 *x9250; int ret; ret = devm_regulator_bulk_get_enable(&spi->dev, ARRAY_SIZE(x9250_regulator_names), x9250_regulator_names); if (ret) return dev_err_probe(&spi->dev, ret, "Failed to get regulators\n"); /* * The x9250 needs a 5ms maximum delay after the power-supplies are set * before performing the first write (1ms for the first read). */ usleep_range(5000, 6000); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*x9250)); if (!indio_dev) return -ENOMEM; x9250 = iio_priv(indio_dev); x9250->spi = spi; x9250->cfg = spi_get_device_match_data(spi); x9250->wp_gpio = devm_gpiod_get_optional(&spi->dev, "wp", GPIOD_OUT_LOW); if (IS_ERR(x9250->wp_gpio)) return dev_err_probe(&spi->dev, PTR_ERR(x9250->wp_gpio), "failed to get wp gpio\n"); indio_dev->info = &x9250_info; indio_dev->channels = x9250_channels; indio_dev->num_channels = ARRAY_SIZE(x9250_channels); indio_dev->name = x9250->cfg->name; return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id x9250_of_match[] = { { .compatible = "renesas,x9250t", .data = &x9250_cfg[X9250T]}, { .compatible = "renesas,x9250u", .data = &x9250_cfg[X9250U]}, { } }; MODULE_DEVICE_TABLE(of, x9250_of_match); static const struct spi_device_id x9250_id_table[] = { { "x9250t", (kernel_ulong_t)&x9250_cfg[X9250T] }, { "x9250u", (kernel_ulong_t)&x9250_cfg[X9250U] }, { } }; MODULE_DEVICE_TABLE(spi, x9250_id_table); static struct spi_driver x9250_spi_driver = { .driver = { .name = "x9250", .of_match_table = x9250_of_match, }, .id_table = x9250_id_table, .probe = x9250_probe, }; module_spi_driver(x9250_spi_driver); MODULE_AUTHOR("Herve Codina "); MODULE_DESCRIPTION("X9250 ALSA SoC driver"); MODULE_LICENSE("GPL");