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, ®, 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