summaryrefslogtreecommitdiffstats
path: root/drivers/iio/common/st_sensors/st_sensors_spi.c
blob: a57cd648975c53f8a71e248401a874e3d984ca95 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// SPDX-License-Identifier: GPL-2.0-only
/*
 * STMicroelectronics sensors spi library driver
 *
 * Copyright 2012-2013 STMicroelectronics Inc.
 *
 * Denis Ciocca <denis.ciocca@st.com>
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>

#include <linux/iio/common/st_sensors_spi.h>


#define ST_SENSORS_SPI_MULTIREAD	0xc0
#define ST_SENSORS_SPI_READ		0x80

static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
{
	struct st_sensor_data *sdata = iio_priv(indio_dev);

	return to_spi_device(sdata->dev)->irq;
}

static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
{
	int err;

	struct spi_transfer xfers[] = {
		{
			.tx_buf = tb->tx_buf,
			.bits_per_word = 8,
			.len = 1,
		},
		{
			.rx_buf = tb->rx_buf,
			.bits_per_word = 8,
			.len = len,
		}
	};

	mutex_lock(&tb->buf_lock);
	if ((multiread_bit) && (len > 1))
		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
	else
		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;

	err = spi_sync_transfer(to_spi_device(dev), xfers, ARRAY_SIZE(xfers));
	if (err)
		goto acc_spi_read_error;

	memcpy(data, tb->rx_buf, len);
	mutex_unlock(&tb->buf_lock);
	return len;

acc_spi_read_error:
	mutex_unlock(&tb->buf_lock);
	return err;
}

static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
				struct device *dev, u8 reg_addr, u8 *res_byte)
{
	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
}

static int st_sensors_spi_read_multiple_byte(
	struct st_sensor_transfer_buffer *tb, struct device *dev,
			u8 reg_addr, int len, u8 *data, bool multiread_bit)
{
	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
}

static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
				struct device *dev, u8 reg_addr, u8 data)
{
	int err;

	struct spi_transfer xfers = {
		.tx_buf = tb->tx_buf,
		.bits_per_word = 8,
		.len = 2,
	};

	mutex_lock(&tb->buf_lock);
	tb->tx_buf[0] = reg_addr;
	tb->tx_buf[1] = data;

	err = spi_sync_transfer(to_spi_device(dev), &xfers, 1);
	mutex_unlock(&tb->buf_lock);

	return err;
}

static const struct st_sensor_transfer_function st_sensors_tf_spi = {
	.read_byte = st_sensors_spi_read_byte,
	.write_byte = st_sensors_spi_write_byte,
	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
};

/*
 * st_sensors_is_spi_3_wire() - check if SPI 3-wire mode has been selected
 * @spi: spi device reference.
 *
 * Return: true if SPI 3-wire mode is selected, false otherwise.
 */
static bool st_sensors_is_spi_3_wire(struct spi_device *spi)
{
	struct device_node *np = spi->dev.of_node;
	struct st_sensors_platform_data *pdata;

	pdata = (struct st_sensors_platform_data *)spi->dev.platform_data;
	if ((np && of_property_read_bool(np, "spi-3wire")) ||
	    (pdata && pdata->spi_3wire)) {
		return true;
	}

	return false;
}

/*
 * st_sensors_configure_spi_3_wire() - configure SPI 3-wire if needed
 * @spi: spi device reference.
 * @settings: sensor specific settings reference.
 *
 * Return: 0 on success, else a negative error code.
 */
static int st_sensors_configure_spi_3_wire(struct spi_device *spi,
					   struct st_sensor_settings *settings)
{
	if (settings->sim.addr) {
		u8 buffer[] = {
			settings->sim.addr,
			settings->sim.value
		};

		return spi_write(spi, buffer, 2);
	}

	return 0;
}

/*
 * st_sensors_spi_configure() - configure SPI interface
 * @indio_dev: IIO device reference.
 * @spi: spi device reference.
 *
 * Return: 0 on success, else a negative error code.
 */
int st_sensors_spi_configure(struct iio_dev *indio_dev,
			     struct spi_device *spi)
{
	struct st_sensor_data *sdata = iio_priv(indio_dev);
	int err;

	if (st_sensors_is_spi_3_wire(spi)) {
		err = st_sensors_configure_spi_3_wire(spi,
						      sdata->sensor_settings);
		if (err < 0)
			return err;
	}

	spi_set_drvdata(spi, indio_dev);

	indio_dev->dev.parent = &spi->dev;
	indio_dev->name = spi->modalias;

	sdata->dev = &spi->dev;
	sdata->tf = &st_sensors_tf_spi;
	sdata->get_irq_data_ready = st_sensors_spi_get_irq;

	return 0;
}
EXPORT_SYMBOL(st_sensors_spi_configure);

MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
MODULE_LICENSE("GPL v2");