162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Enter bugs at http://blackfin.uclinux.org/ 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/input.h> /* BUS_SPI */ 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/spi/spi.h> 1362306a36Sopenharmony_ci#include <linux/pm.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include "adxl34x.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define MAX_SPI_FREQ_HZ 5000000 1862306a36Sopenharmony_ci#define MAX_FREQ_NO_FIFODELAY 1500000 1962306a36Sopenharmony_ci#define ADXL34X_CMD_MULTB (1 << 6) 2062306a36Sopenharmony_ci#define ADXL34X_CMD_READ (1 << 7) 2162306a36Sopenharmony_ci#define ADXL34X_WRITECMD(reg) (reg & 0x3F) 2262306a36Sopenharmony_ci#define ADXL34X_READCMD(reg) (ADXL34X_CMD_READ | (reg & 0x3F)) 2362306a36Sopenharmony_ci#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \ 2462306a36Sopenharmony_ci | (reg & 0x3F)) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int adxl34x_spi_read(struct device *dev, unsigned char reg) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 2962306a36Sopenharmony_ci unsigned char cmd; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci cmd = ADXL34X_READCMD(reg); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci return spi_w8r8(spi, cmd); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int adxl34x_spi_write(struct device *dev, 3762306a36Sopenharmony_ci unsigned char reg, unsigned char val) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 4062306a36Sopenharmony_ci unsigned char buf[2]; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci buf[0] = ADXL34X_WRITECMD(reg); 4362306a36Sopenharmony_ci buf[1] = val; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return spi_write(spi, buf, sizeof(buf)); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int adxl34x_spi_read_block(struct device *dev, 4962306a36Sopenharmony_ci unsigned char reg, int count, 5062306a36Sopenharmony_ci void *buf) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 5362306a36Sopenharmony_ci ssize_t status; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci reg = ADXL34X_READMB_CMD(reg); 5662306a36Sopenharmony_ci status = spi_write_then_read(spi, ®, 1, buf, count); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return (status < 0) ? status : 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct adxl34x_bus_ops adxl34x_spi_bops = { 6262306a36Sopenharmony_ci .bustype = BUS_SPI, 6362306a36Sopenharmony_ci .write = adxl34x_spi_write, 6462306a36Sopenharmony_ci .read = adxl34x_spi_read, 6562306a36Sopenharmony_ci .read_block = adxl34x_spi_read_block, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int adxl34x_spi_probe(struct spi_device *spi) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct adxl34x *ac; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* don't exceed max specified SPI CLK frequency */ 7362306a36Sopenharmony_ci if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { 7462306a36Sopenharmony_ci dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz); 7562306a36Sopenharmony_ci return -EINVAL; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci ac = adxl34x_probe(&spi->dev, spi->irq, 7962306a36Sopenharmony_ci spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY, 8062306a36Sopenharmony_ci &adxl34x_spi_bops); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (IS_ERR(ac)) 8362306a36Sopenharmony_ci return PTR_ERR(ac); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci spi_set_drvdata(spi, ac); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void adxl34x_spi_remove(struct spi_device *spi) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct adxl34x *ac = spi_get_drvdata(spi); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci adxl34x_remove(ac); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct spi_driver adxl34x_driver = { 9862306a36Sopenharmony_ci .driver = { 9962306a36Sopenharmony_ci .name = "adxl34x", 10062306a36Sopenharmony_ci .pm = pm_sleep_ptr(&adxl34x_pm), 10162306a36Sopenharmony_ci }, 10262306a36Sopenharmony_ci .probe = adxl34x_spi_probe, 10362306a36Sopenharmony_ci .remove = adxl34x_spi_remove, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cimodule_spi_driver(adxl34x_driver); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciMODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 10962306a36Sopenharmony_ciMODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver"); 11062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 111