1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Register map access API - SPI support |
4 | // |
5 | // Copyright 2011 Wolfson Microelectronics plc |
6 | // |
7 | // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
8 | |
9 | #include <linux/regmap.h> |
10 | #include <linux/spi/spi.h> |
11 | #include <linux/module.h> |
12 | |
13 | #include "internal.h" |
14 | |
15 | struct regmap_async_spi { |
16 | struct regmap_async core; |
17 | struct spi_message m; |
18 | struct spi_transfer t[2]; |
19 | }; |
20 | |
21 | static void regmap_spi_complete(void *data) |
22 | { |
23 | struct regmap_async_spi *async = data; |
24 | |
25 | regmap_async_complete_cb(async: &async->core, ret: async->m.status); |
26 | } |
27 | |
28 | static int regmap_spi_write(void *context, const void *data, size_t count) |
29 | { |
30 | struct device *dev = context; |
31 | struct spi_device *spi = to_spi_device(dev); |
32 | |
33 | return spi_write(spi, buf: data, len: count); |
34 | } |
35 | |
36 | static int regmap_spi_gather_write(void *context, |
37 | const void *reg, size_t reg_len, |
38 | const void *val, size_t val_len) |
39 | { |
40 | struct device *dev = context; |
41 | struct spi_device *spi = to_spi_device(dev); |
42 | struct spi_message m; |
43 | struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, }, |
44 | { .tx_buf = val, .len = val_len, }, }; |
45 | |
46 | spi_message_init(m: &m); |
47 | spi_message_add_tail(t: &t[0], m: &m); |
48 | spi_message_add_tail(t: &t[1], m: &m); |
49 | |
50 | return spi_sync(spi, message: &m); |
51 | } |
52 | |
53 | static int regmap_spi_async_write(void *context, |
54 | const void *reg, size_t reg_len, |
55 | const void *val, size_t val_len, |
56 | struct regmap_async *a) |
57 | { |
58 | struct regmap_async_spi *async = container_of(a, |
59 | struct regmap_async_spi, |
60 | core); |
61 | struct device *dev = context; |
62 | struct spi_device *spi = to_spi_device(dev); |
63 | |
64 | async->t[0].tx_buf = reg; |
65 | async->t[0].len = reg_len; |
66 | async->t[1].tx_buf = val; |
67 | async->t[1].len = val_len; |
68 | |
69 | spi_message_init(m: &async->m); |
70 | spi_message_add_tail(t: &async->t[0], m: &async->m); |
71 | if (val) |
72 | spi_message_add_tail(t: &async->t[1], m: &async->m); |
73 | |
74 | async->m.complete = regmap_spi_complete; |
75 | async->m.context = async; |
76 | |
77 | return spi_async(spi, message: &async->m); |
78 | } |
79 | |
80 | static struct regmap_async *regmap_spi_async_alloc(void) |
81 | { |
82 | struct regmap_async_spi *async_spi; |
83 | |
84 | async_spi = kzalloc(size: sizeof(*async_spi), GFP_KERNEL); |
85 | if (!async_spi) |
86 | return NULL; |
87 | |
88 | return &async_spi->core; |
89 | } |
90 | |
91 | static int regmap_spi_read(void *context, |
92 | const void *reg, size_t reg_size, |
93 | void *val, size_t val_size) |
94 | { |
95 | struct device *dev = context; |
96 | struct spi_device *spi = to_spi_device(dev); |
97 | |
98 | return spi_write_then_read(spi, txbuf: reg, n_tx: reg_size, rxbuf: val, n_rx: val_size); |
99 | } |
100 | |
101 | static const struct regmap_bus regmap_spi = { |
102 | .write = regmap_spi_write, |
103 | .gather_write = regmap_spi_gather_write, |
104 | .async_write = regmap_spi_async_write, |
105 | .async_alloc = regmap_spi_async_alloc, |
106 | .read = regmap_spi_read, |
107 | .read_flag_mask = 0x80, |
108 | .reg_format_endian_default = REGMAP_ENDIAN_BIG, |
109 | .val_format_endian_default = REGMAP_ENDIAN_BIG, |
110 | }; |
111 | |
112 | static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi, |
113 | const struct regmap_config *config) |
114 | { |
115 | size_t max_size = spi_max_transfer_size(spi); |
116 | size_t max_msg_size, reg_reserve_size; |
117 | struct regmap_bus *bus; |
118 | |
119 | if (max_size != SIZE_MAX) { |
120 | bus = kmemdup(p: ®map_spi, size: sizeof(*bus), GFP_KERNEL); |
121 | if (!bus) |
122 | return ERR_PTR(error: -ENOMEM); |
123 | |
124 | max_msg_size = spi_max_message_size(spi); |
125 | reg_reserve_size = config->reg_bits / BITS_PER_BYTE |
126 | + config->pad_bits / BITS_PER_BYTE; |
127 | if (max_size + reg_reserve_size > max_msg_size) |
128 | max_size -= reg_reserve_size; |
129 | |
130 | bus->free_on_exit = true; |
131 | bus->max_raw_read = max_size; |
132 | bus->max_raw_write = max_size; |
133 | |
134 | return bus; |
135 | } |
136 | |
137 | return ®map_spi; |
138 | } |
139 | |
140 | struct regmap *__regmap_init_spi(struct spi_device *spi, |
141 | const struct regmap_config *config, |
142 | struct lock_class_key *lock_key, |
143 | const char *lock_name) |
144 | { |
145 | const struct regmap_bus *bus = regmap_get_spi_bus(spi, config); |
146 | |
147 | if (IS_ERR(ptr: bus)) |
148 | return ERR_CAST(ptr: bus); |
149 | |
150 | return __regmap_init(dev: &spi->dev, bus, bus_context: &spi->dev, config, lock_key, lock_name); |
151 | } |
152 | EXPORT_SYMBOL_GPL(__regmap_init_spi); |
153 | |
154 | struct regmap *__devm_regmap_init_spi(struct spi_device *spi, |
155 | const struct regmap_config *config, |
156 | struct lock_class_key *lock_key, |
157 | const char *lock_name) |
158 | { |
159 | const struct regmap_bus *bus = regmap_get_spi_bus(spi, config); |
160 | |
161 | if (IS_ERR(ptr: bus)) |
162 | return ERR_CAST(ptr: bus); |
163 | |
164 | return __devm_regmap_init(dev: &spi->dev, bus, bus_context: &spi->dev, config, lock_key, lock_name); |
165 | } |
166 | EXPORT_SYMBOL_GPL(__devm_regmap_init_spi); |
167 | |
168 | MODULE_LICENSE("GPL" ); |
169 | |