summaryrefslogtreecommitdiffstats
path: root/drivers/input/misc/adxl34x-spi.c
blob: 7f992353ffdd0217b6121d581908cf538b1f6f2c (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
/*
 * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface)
 *
 * Enter bugs at http://blackfin.uclinux.org/
 *
 * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
 * Licensed under the GPL-2 or later.
 */

#include <linux/input.h>	/* BUS_SPI */
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include "adxl34x.h"

#define MAX_SPI_FREQ_HZ		5000000
#define MAX_FREQ_NO_FIFODELAY	1500000
#define ADXL34X_CMD_MULTB	(1 << 6)
#define ADXL34X_CMD_READ	(1 << 7)
#define ADXL34X_WRITECMD(reg)	(reg & 0x3F)
#define ADXL34X_READCMD(reg)	(ADXL34X_CMD_READ | (reg & 0x3F))
#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \
					| (reg & 0x3F))

static int adxl34x_spi_read(struct device *dev, unsigned char reg)
{
	struct spi_device *spi = to_spi_device(dev);
	unsigned char cmd;

	cmd = ADXL34X_READCMD(reg);

	return spi_w8r8(spi, cmd);
}

static int adxl34x_spi_write(struct device *dev,
			     unsigned char reg, unsigned char val)
{
	struct spi_device *spi = to_spi_device(dev);
	unsigned char buf[2];

	buf[0] = ADXL34X_WRITECMD(reg);
	buf[1] = val;

	return spi_write(spi, buf, sizeof(buf));
}

static int adxl34x_spi_read_block(struct device *dev,
				  unsigned char reg, int count,
				  void *buf)
{
	struct spi_device *spi = to_spi_device(dev);
	ssize_t status;

	reg = ADXL34X_READMB_CMD(reg);
	status = spi_write_then_read(spi, &reg, 1, buf, count);

	return (status < 0) ? status : 0;
}

static const struct adxl34x_bus_ops adx134x_spi_bops = {
	.bustype	= BUS_SPI,
	.write		= adxl34x_spi_write,
	.read		= adxl34x_spi_read,
	.read_block	= adxl34x_spi_read_block,
};

static int __devinit adxl34x_spi_probe(struct spi_device *spi)
{
	struct adxl34x *ac;

	/* don't exceed max specified SPI CLK frequency */
	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
		dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz);
		return -EINVAL;
	}

	ac = adxl34x_probe(&spi->dev, spi->irq,
			   spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY,
			   &adx134x_spi_bops);

	if (IS_ERR(ac))
		return PTR_ERR(ac);

	spi_set_drvdata(spi, ac);

	return 0;
}

static int __devexit adxl34x_spi_remove(struct spi_device *spi)
{
	struct adxl34x *ac = dev_get_drvdata(&spi->dev);

	return adxl34x_remove(ac);
}

#ifdef CONFIG_PM
static int adxl34x_suspend(struct spi_device *spi, pm_message_t message)
{
	struct adxl34x *ac = dev_get_drvdata(&spi->dev);

	adxl34x_disable(ac);

	return 0;
}

static int adxl34x_resume(struct spi_device *spi)
{
	struct adxl34x *ac = dev_get_drvdata(&spi->dev);

	adxl34x_enable(ac);

	return 0;
}
#else
# define adxl34x_suspend NULL
# define adxl34x_resume  NULL
#endif

static struct spi_driver adxl34x_driver = {
	.driver = {
		.name = "adxl34x",
		.bus = &spi_bus_type,
		.owner = THIS_MODULE,
	},
	.probe   = adxl34x_spi_probe,
	.remove  = __devexit_p(adxl34x_spi_remove),
	.suspend = adxl34x_suspend,
	.resume  = adxl34x_resume,
};

static int __init adxl34x_spi_init(void)
{
	return spi_register_driver(&adxl34x_driver);
}
module_init(adxl34x_spi_init);

static void __exit adxl34x_spi_exit(void)
{
	spi_unregister_driver(&adxl34x_driver);
}
module_exit(adxl34x_spi_exit);

MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver");
MODULE_LICENSE("GPL");