18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Enter bugs at http://blackfin.uclinux.org/
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/input.h>	/* BUS_SPI */
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
138c2ecf20Sopenharmony_ci#include <linux/pm.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include "adxl34x.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define MAX_SPI_FREQ_HZ		5000000
188c2ecf20Sopenharmony_ci#define MAX_FREQ_NO_FIFODELAY	1500000
198c2ecf20Sopenharmony_ci#define ADXL34X_CMD_MULTB	(1 << 6)
208c2ecf20Sopenharmony_ci#define ADXL34X_CMD_READ	(1 << 7)
218c2ecf20Sopenharmony_ci#define ADXL34X_WRITECMD(reg)	(reg & 0x3F)
228c2ecf20Sopenharmony_ci#define ADXL34X_READCMD(reg)	(ADXL34X_CMD_READ | (reg & 0x3F))
238c2ecf20Sopenharmony_ci#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \
248c2ecf20Sopenharmony_ci					| (reg & 0x3F))
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int adxl34x_spi_read(struct device *dev, unsigned char reg)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
298c2ecf20Sopenharmony_ci	unsigned char cmd;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	cmd = ADXL34X_READCMD(reg);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	return spi_w8r8(spi, cmd);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int adxl34x_spi_write(struct device *dev,
378c2ecf20Sopenharmony_ci			     unsigned char reg, unsigned char val)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
408c2ecf20Sopenharmony_ci	unsigned char buf[2];
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	buf[0] = ADXL34X_WRITECMD(reg);
438c2ecf20Sopenharmony_ci	buf[1] = val;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return spi_write(spi, buf, sizeof(buf));
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int adxl34x_spi_read_block(struct device *dev,
498c2ecf20Sopenharmony_ci				  unsigned char reg, int count,
508c2ecf20Sopenharmony_ci				  void *buf)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
538c2ecf20Sopenharmony_ci	ssize_t status;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	reg = ADXL34X_READMB_CMD(reg);
568c2ecf20Sopenharmony_ci	status = spi_write_then_read(spi, &reg, 1, buf, count);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return (status < 0) ? status : 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct adxl34x_bus_ops adxl34x_spi_bops = {
628c2ecf20Sopenharmony_ci	.bustype	= BUS_SPI,
638c2ecf20Sopenharmony_ci	.write		= adxl34x_spi_write,
648c2ecf20Sopenharmony_ci	.read		= adxl34x_spi_read,
658c2ecf20Sopenharmony_ci	.read_block	= adxl34x_spi_read_block,
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int adxl34x_spi_probe(struct spi_device *spi)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct adxl34x *ac;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* don't exceed max specified SPI CLK frequency */
738c2ecf20Sopenharmony_ci	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
748c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz);
758c2ecf20Sopenharmony_ci		return -EINVAL;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ac = adxl34x_probe(&spi->dev, spi->irq,
798c2ecf20Sopenharmony_ci			   spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY,
808c2ecf20Sopenharmony_ci			   &adxl34x_spi_bops);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (IS_ERR(ac))
838c2ecf20Sopenharmony_ci		return PTR_ERR(ac);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, ac);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int adxl34x_spi_remove(struct spi_device *spi)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct adxl34x *ac = spi_get_drvdata(spi);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return adxl34x_remove(ac);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int __maybe_unused adxl34x_spi_suspend(struct device *dev)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
1008c2ecf20Sopenharmony_ci	struct adxl34x *ac = spi_get_drvdata(spi);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	adxl34x_suspend(ac);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int __maybe_unused adxl34x_spi_resume(struct device *dev)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
1108c2ecf20Sopenharmony_ci	struct adxl34x *ac = spi_get_drvdata(spi);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	adxl34x_resume(ac);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend,
1188c2ecf20Sopenharmony_ci			 adxl34x_spi_resume);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct spi_driver adxl34x_driver = {
1218c2ecf20Sopenharmony_ci	.driver = {
1228c2ecf20Sopenharmony_ci		.name = "adxl34x",
1238c2ecf20Sopenharmony_ci		.pm = &adxl34x_spi_pm,
1248c2ecf20Sopenharmony_ci	},
1258c2ecf20Sopenharmony_ci	.probe   = adxl34x_spi_probe,
1268c2ecf20Sopenharmony_ci	.remove  = adxl34x_spi_remove,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cimodule_spi_driver(adxl34x_driver);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
1328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver");
1338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
134