1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * STMicroelectronics accelerometers driver
4 *
5 * Copyright 2012-2013 STMicroelectronics Inc.
6 *
7 * Denis Ciocca <denis.ciocca@st.com>
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13#include <linux/acpi.h>
14#include <linux/i2c.h>
15#include <linux/iio/iio.h>
16#include <linux/property.h>
17
18#include <linux/iio/common/st_sensors_i2c.h>
19#include "st_accel.h"
20
21static const struct of_device_id st_accel_of_match[] = {
22	{
23		/* An older compatible */
24		.compatible = "st,lis3lv02d",
25		.data = LIS3LV02DL_ACCEL_DEV_NAME,
26	},
27	{
28		.compatible = "st,lis3lv02dl-accel",
29		.data = LIS3LV02DL_ACCEL_DEV_NAME,
30	},
31	{
32		.compatible = "st,lsm303dlh-accel",
33		.data = LSM303DLH_ACCEL_DEV_NAME,
34	},
35	{
36		.compatible = "st,lsm303dlhc-accel",
37		.data = LSM303DLHC_ACCEL_DEV_NAME,
38	},
39	{
40		.compatible = "st,lis3dh-accel",
41		.data = LIS3DH_ACCEL_DEV_NAME,
42	},
43	{
44		.compatible = "st,lsm330d-accel",
45		.data = LSM330D_ACCEL_DEV_NAME,
46	},
47	{
48		.compatible = "st,lsm330dl-accel",
49		.data = LSM330DL_ACCEL_DEV_NAME,
50	},
51	{
52		.compatible = "st,lsm330dlc-accel",
53		.data = LSM330DLC_ACCEL_DEV_NAME,
54	},
55	{
56		.compatible = "st,lis331dl-accel",
57		.data = LIS331DL_ACCEL_DEV_NAME,
58	},
59	{
60		.compatible = "st,lis331dlh-accel",
61		.data = LIS331DLH_ACCEL_DEV_NAME,
62	},
63	{
64		.compatible = "st,lsm303dl-accel",
65		.data = LSM303DL_ACCEL_DEV_NAME,
66	},
67	{
68		.compatible = "st,lsm303dlm-accel",
69		.data = LSM303DLM_ACCEL_DEV_NAME,
70	},
71	{
72		.compatible = "st,lsm330-accel",
73		.data = LSM330_ACCEL_DEV_NAME,
74	},
75	{
76		.compatible = "st,lsm303agr-accel",
77		.data = LSM303AGR_ACCEL_DEV_NAME,
78	},
79	{
80		.compatible = "st,lis2dh12-accel",
81		.data = LIS2DH12_ACCEL_DEV_NAME,
82	},
83	{
84		.compatible = "st,h3lis331dl-accel",
85		.data = H3LIS331DL_ACCEL_DEV_NAME,
86	},
87	{
88		.compatible = "st,lis3l02dq",
89		.data = LIS3L02DQ_ACCEL_DEV_NAME,
90	},
91	{
92		.compatible = "st,lng2dm-accel",
93		.data = LNG2DM_ACCEL_DEV_NAME,
94	},
95	{
96		.compatible = "st,lis2dw12",
97		.data = LIS2DW12_ACCEL_DEV_NAME,
98	},
99	{
100		.compatible = "st,lis3de",
101		.data = LIS3DE_ACCEL_DEV_NAME,
102	},
103	{
104		.compatible = "st,lis2de12",
105		.data = LIS2DE12_ACCEL_DEV_NAME,
106	},
107	{
108		.compatible = "st,lis2hh12",
109		.data = LIS2HH12_ACCEL_DEV_NAME,
110	},
111	{},
112};
113MODULE_DEVICE_TABLE(of, st_accel_of_match);
114
115#ifdef CONFIG_ACPI
116static const struct acpi_device_id st_accel_acpi_match[] = {
117	{"SMO8840", (kernel_ulong_t)LIS2DH12_ACCEL_DEV_NAME},
118	{"SMO8A90", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME},
119	{ },
120};
121MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
122#endif
123
124static const struct i2c_device_id st_accel_id_table[] = {
125	{ LSM303DLH_ACCEL_DEV_NAME },
126	{ LSM303DLHC_ACCEL_DEV_NAME },
127	{ LIS3DH_ACCEL_DEV_NAME },
128	{ LSM330D_ACCEL_DEV_NAME },
129	{ LSM330DL_ACCEL_DEV_NAME },
130	{ LSM330DLC_ACCEL_DEV_NAME },
131	{ LIS331DLH_ACCEL_DEV_NAME },
132	{ LSM303DL_ACCEL_DEV_NAME },
133	{ LSM303DLM_ACCEL_DEV_NAME },
134	{ LSM330_ACCEL_DEV_NAME },
135	{ LSM303AGR_ACCEL_DEV_NAME },
136	{ LIS2DH12_ACCEL_DEV_NAME },
137	{ LIS3L02DQ_ACCEL_DEV_NAME },
138	{ LNG2DM_ACCEL_DEV_NAME },
139	{ H3LIS331DL_ACCEL_DEV_NAME },
140	{ LIS331DL_ACCEL_DEV_NAME },
141	{ LIS3LV02DL_ACCEL_DEV_NAME },
142	{ LIS2DW12_ACCEL_DEV_NAME },
143	{ LIS3DE_ACCEL_DEV_NAME },
144	{ LIS2DE12_ACCEL_DEV_NAME },
145	{ LIS2HH12_ACCEL_DEV_NAME },
146	{},
147};
148MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
149
150static int st_accel_i2c_probe(struct i2c_client *client)
151{
152	const struct st_sensor_settings *settings;
153	struct st_sensor_data *adata;
154	struct iio_dev *indio_dev;
155	int ret;
156
157	st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name));
158
159	settings = st_accel_get_settings(client->name);
160	if (!settings) {
161		dev_err(&client->dev, "device name %s not recognized.\n",
162			client->name);
163		return -ENODEV;
164	}
165
166	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
167	if (!indio_dev)
168		return -ENOMEM;
169
170	adata = iio_priv(indio_dev);
171	adata->sensor_settings = (struct st_sensor_settings *)settings;
172
173	ret = st_sensors_i2c_configure(indio_dev, client);
174	if (ret < 0)
175		return ret;
176
177	ret = st_sensors_power_enable(indio_dev);
178	if (ret)
179		return ret;
180
181	ret = st_accel_common_probe(indio_dev);
182	if (ret < 0)
183		goto st_accel_power_off;
184
185	return 0;
186
187st_accel_power_off:
188	st_sensors_power_disable(indio_dev);
189
190	return ret;
191}
192
193static int st_accel_i2c_remove(struct i2c_client *client)
194{
195	struct iio_dev *indio_dev = i2c_get_clientdata(client);
196
197	st_accel_common_remove(indio_dev);
198
199	st_sensors_power_disable(indio_dev);
200
201	return 0;
202}
203
204static struct i2c_driver st_accel_driver = {
205	.driver = {
206		.name = "st-accel-i2c",
207		.of_match_table = st_accel_of_match,
208		.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
209	},
210	.probe_new = st_accel_i2c_probe,
211	.remove = st_accel_i2c_remove,
212	.id_table = st_accel_id_table,
213};
214module_i2c_driver(st_accel_driver);
215
216MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
217MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
218MODULE_LICENSE("GPL v2");
219