1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ST Microelectronics MFD: stmpe's spi client specific driver |
4 | * |
5 | * Copyright (C) ST Microelectronics SA 2011 |
6 | * |
7 | * Author: Viresh Kumar <vireshk@kernel.org> for ST Microelectronics |
8 | */ |
9 | |
10 | #include <linux/spi/spi.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/types.h> |
16 | #include "stmpe.h" |
17 | |
18 | #define READ_CMD (1 << 7) |
19 | |
20 | static int spi_reg_read(struct stmpe *stmpe, u8 reg) |
21 | { |
22 | struct spi_device *spi = stmpe->client; |
23 | int status = spi_w8r16(spi, cmd: reg | READ_CMD); |
24 | |
25 | return (status < 0) ? status : status >> 8; |
26 | } |
27 | |
28 | static int spi_reg_write(struct stmpe *stmpe, u8 reg, u8 val) |
29 | { |
30 | struct spi_device *spi = stmpe->client; |
31 | u16 cmd = (val << 8) | reg; |
32 | |
33 | return spi_write(spi, buf: (const u8 *)&cmd, len: 2); |
34 | } |
35 | |
36 | static int spi_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values) |
37 | { |
38 | int ret, i; |
39 | |
40 | for (i = 0; i < length; i++) { |
41 | ret = spi_reg_read(stmpe, reg: reg + i); |
42 | if (ret < 0) |
43 | return ret; |
44 | *(values + i) = ret; |
45 | } |
46 | |
47 | return 0; |
48 | } |
49 | |
50 | static int spi_block_write(struct stmpe *stmpe, u8 reg, u8 length, |
51 | const u8 *values) |
52 | { |
53 | int ret = 0, i; |
54 | |
55 | for (i = length; i > 0; i--, reg++) { |
56 | ret = spi_reg_write(stmpe, reg, val: *(values + i - 1)); |
57 | if (ret < 0) |
58 | return ret; |
59 | } |
60 | |
61 | return ret; |
62 | } |
63 | |
64 | static void spi_init(struct stmpe *stmpe) |
65 | { |
66 | struct spi_device *spi = stmpe->client; |
67 | |
68 | spi->bits_per_word = 8; |
69 | |
70 | /* This register is only present for stmpe811 */ |
71 | if (stmpe->variant->id_val == 0x0811) |
72 | spi_reg_write(stmpe, STMPE811_REG_SPI_CFG, val: spi->mode); |
73 | |
74 | if (spi_setup(spi) < 0) |
75 | dev_dbg(&spi->dev, "spi_setup failed\n" ); |
76 | } |
77 | |
78 | static struct stmpe_client_info spi_ci = { |
79 | .read_byte = spi_reg_read, |
80 | .write_byte = spi_reg_write, |
81 | .read_block = spi_block_read, |
82 | .write_block = spi_block_write, |
83 | .init = spi_init, |
84 | }; |
85 | |
86 | static int |
87 | stmpe_spi_probe(struct spi_device *spi) |
88 | { |
89 | const struct spi_device_id *id = spi_get_device_id(sdev: spi); |
90 | |
91 | /* don't exceed max specified rate - 1MHz - Limitation of STMPE */ |
92 | if (spi->max_speed_hz > 1000000) { |
93 | dev_dbg(&spi->dev, "f(sample) %d KHz?\n" , |
94 | (spi->max_speed_hz/1000)); |
95 | return -EINVAL; |
96 | } |
97 | |
98 | spi_ci.irq = spi->irq; |
99 | spi_ci.client = spi; |
100 | spi_ci.dev = &spi->dev; |
101 | |
102 | return stmpe_probe(ci: &spi_ci, partnum: id->driver_data); |
103 | } |
104 | |
105 | static void stmpe_spi_remove(struct spi_device *spi) |
106 | { |
107 | struct stmpe *stmpe = spi_get_drvdata(spi); |
108 | |
109 | stmpe_remove(stmpe); |
110 | } |
111 | |
112 | static const struct of_device_id stmpe_spi_of_match[] = { |
113 | { .compatible = "st,stmpe610" , }, |
114 | { .compatible = "st,stmpe801" , }, |
115 | { .compatible = "st,stmpe811" , }, |
116 | { .compatible = "st,stmpe1601" , }, |
117 | { .compatible = "st,stmpe2401" , }, |
118 | { .compatible = "st,stmpe2403" , }, |
119 | { /* sentinel */ }, |
120 | }; |
121 | MODULE_DEVICE_TABLE(of, stmpe_spi_of_match); |
122 | |
123 | static const struct spi_device_id stmpe_spi_id[] = { |
124 | { "stmpe610" , STMPE610 }, |
125 | { "stmpe801" , STMPE801 }, |
126 | { "stmpe811" , STMPE811 }, |
127 | { "stmpe1601" , STMPE1601 }, |
128 | { "stmpe2401" , STMPE2401 }, |
129 | { "stmpe2403" , STMPE2403 }, |
130 | { } |
131 | }; |
132 | MODULE_DEVICE_TABLE(spi, stmpe_id); |
133 | |
134 | static struct spi_driver stmpe_spi_driver = { |
135 | .driver = { |
136 | .name = "stmpe-spi" , |
137 | .of_match_table = of_match_ptr(stmpe_spi_of_match), |
138 | .pm = pm_sleep_ptr(&stmpe_dev_pm_ops), |
139 | }, |
140 | .probe = stmpe_spi_probe, |
141 | .remove = stmpe_spi_remove, |
142 | .id_table = stmpe_spi_id, |
143 | }; |
144 | |
145 | static int __init stmpe_init(void) |
146 | { |
147 | return spi_register_driver(&stmpe_spi_driver); |
148 | } |
149 | subsys_initcall(stmpe_init); |
150 | |
151 | static void __exit stmpe_exit(void) |
152 | { |
153 | spi_unregister_driver(sdrv: &stmpe_spi_driver); |
154 | } |
155 | module_exit(stmpe_exit); |
156 | |
157 | MODULE_DESCRIPTION("STMPE MFD SPI Interface Driver" ); |
158 | MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>" ); |
159 | |