137613fa5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
237613fa5SGreg Kroah-Hartman //
337613fa5SGreg Kroah-Hartman // Register map access API - SPI support
437613fa5SGreg Kroah-Hartman //
537613fa5SGreg Kroah-Hartman // Copyright 2011 Wolfson Microelectronics plc
637613fa5SGreg Kroah-Hartman //
737613fa5SGreg Kroah-Hartman // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8a676f083SMark Brown
9a676f083SMark Brown #include <linux/regmap.h>
10a676f083SMark Brown #include <linux/spi/spi.h>
11b5ddbf46SStephen Rothwell #include <linux/module.h>
12a676f083SMark Brown
13e0356dfeSMark Brown #include "internal.h"
14e0356dfeSMark Brown
15e0356dfeSMark Brown struct regmap_async_spi {
16e0356dfeSMark Brown struct regmap_async core;
17e0356dfeSMark Brown struct spi_message m;
18e0356dfeSMark Brown struct spi_transfer t[2];
19e0356dfeSMark Brown };
20e0356dfeSMark Brown
regmap_spi_complete(void * data)21e0356dfeSMark Brown static void regmap_spi_complete(void *data)
22e0356dfeSMark Brown {
23e0356dfeSMark Brown struct regmap_async_spi *async = data;
24e0356dfeSMark Brown
25e0356dfeSMark Brown regmap_async_complete_cb(&async->core, async->m.status);
26e0356dfeSMark Brown }
27e0356dfeSMark Brown
regmap_spi_write(void * context,const void * data,size_t count)280135bbccSStephen Warren static int regmap_spi_write(void *context, const void *data, size_t count)
29a676f083SMark Brown {
300135bbccSStephen Warren struct device *dev = context;
31a676f083SMark Brown struct spi_device *spi = to_spi_device(dev);
32a676f083SMark Brown
33a676f083SMark Brown return spi_write(spi, data, count);
34a676f083SMark Brown }
35a676f083SMark Brown
regmap_spi_gather_write(void * context,const void * reg,size_t reg_len,const void * val,size_t val_len)360135bbccSStephen Warren static int regmap_spi_gather_write(void *context,
37a676f083SMark Brown const void *reg, size_t reg_len,
38a676f083SMark Brown const void *val, size_t val_len)
39a676f083SMark Brown {
400135bbccSStephen Warren struct device *dev = context;
41a676f083SMark Brown struct spi_device *spi = to_spi_device(dev);
42a676f083SMark Brown struct spi_message m;
43a676f083SMark Brown struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, },
44a676f083SMark Brown { .tx_buf = val, .len = val_len, }, };
45a676f083SMark Brown
46a676f083SMark Brown spi_message_init(&m);
47a676f083SMark Brown spi_message_add_tail(&t[0], &m);
48a676f083SMark Brown spi_message_add_tail(&t[1], &m);
49a676f083SMark Brown
50a676f083SMark Brown return spi_sync(spi, &m);
51a676f083SMark Brown }
52a676f083SMark Brown
regmap_spi_async_write(void * context,const void * reg,size_t reg_len,const void * val,size_t val_len,struct regmap_async * a)53e0356dfeSMark Brown static int regmap_spi_async_write(void *context,
54e0356dfeSMark Brown const void *reg, size_t reg_len,
55e0356dfeSMark Brown const void *val, size_t val_len,
56e0356dfeSMark Brown struct regmap_async *a)
57e0356dfeSMark Brown {
58e0356dfeSMark Brown struct regmap_async_spi *async = container_of(a,
59e0356dfeSMark Brown struct regmap_async_spi,
60e0356dfeSMark Brown core);
61e0356dfeSMark Brown struct device *dev = context;
62e0356dfeSMark Brown struct spi_device *spi = to_spi_device(dev);
63e0356dfeSMark Brown
64e0356dfeSMark Brown async->t[0].tx_buf = reg;
65e0356dfeSMark Brown async->t[0].len = reg_len;
66e0356dfeSMark Brown async->t[1].tx_buf = val;
67e0356dfeSMark Brown async->t[1].len = val_len;
68e0356dfeSMark Brown
69e0356dfeSMark Brown spi_message_init(&async->m);
70e0356dfeSMark Brown spi_message_add_tail(&async->t[0], &async->m);
71cd1b9dd0SMark Brown if (val)
72e0356dfeSMark Brown spi_message_add_tail(&async->t[1], &async->m);
73e0356dfeSMark Brown
74e0356dfeSMark Brown async->m.complete = regmap_spi_complete;
75e0356dfeSMark Brown async->m.context = async;
76e0356dfeSMark Brown
77e0356dfeSMark Brown return spi_async(spi, &async->m);
78e0356dfeSMark Brown }
79e0356dfeSMark Brown
regmap_spi_async_alloc(void)80e0356dfeSMark Brown static struct regmap_async *regmap_spi_async_alloc(void)
81e0356dfeSMark Brown {
82e0356dfeSMark Brown struct regmap_async_spi *async_spi;
83e0356dfeSMark Brown
84e0356dfeSMark Brown async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL);
8595601d65SMark Brown if (!async_spi)
8695601d65SMark Brown return NULL;
87e0356dfeSMark Brown
88e0356dfeSMark Brown return &async_spi->core;
89e0356dfeSMark Brown }
90e0356dfeSMark Brown
regmap_spi_read(void * context,const void * reg,size_t reg_size,void * val,size_t val_size)910135bbccSStephen Warren static int regmap_spi_read(void *context,
92a676f083SMark Brown const void *reg, size_t reg_size,
93a676f083SMark Brown void *val, size_t val_size)
94a676f083SMark Brown {
950135bbccSStephen Warren struct device *dev = context;
96a676f083SMark Brown struct spi_device *spi = to_spi_device(dev);
97a676f083SMark Brown
98a676f083SMark Brown return spi_write_then_read(spi, reg, reg_size, val, val_size);
99a676f083SMark Brown }
100a676f083SMark Brown
1019c2e5cb3SJulia Lawall static const struct regmap_bus regmap_spi = {
102a676f083SMark Brown .write = regmap_spi_write,
103a676f083SMark Brown .gather_write = regmap_spi_gather_write,
104e0356dfeSMark Brown .async_write = regmap_spi_async_write,
105e0356dfeSMark Brown .async_alloc = regmap_spi_async_alloc,
106a676f083SMark Brown .read = regmap_spi_read,
107a676f083SMark Brown .read_flag_mask = 0x80,
108d647c199SXiubo Li .reg_format_endian_default = REGMAP_ENDIAN_BIG,
109d647c199SXiubo Li .val_format_endian_default = REGMAP_ENDIAN_BIG,
110a676f083SMark Brown };
111a676f083SMark Brown
regmap_get_spi_bus(struct spi_device * spi,const struct regmap_config * config)112f231ff38SLucas Tanure static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,
113f231ff38SLucas Tanure const struct regmap_config *config)
114f231ff38SLucas Tanure {
115f231ff38SLucas Tanure size_t max_size = spi_max_transfer_size(spi);
116f5723cfcSCristian Ciocaltea size_t max_msg_size, reg_reserve_size;
117f231ff38SLucas Tanure struct regmap_bus *bus;
118f231ff38SLucas Tanure
119f231ff38SLucas Tanure if (max_size != SIZE_MAX) {
120f231ff38SLucas Tanure bus = kmemdup(®map_spi, sizeof(*bus), GFP_KERNEL);
121f231ff38SLucas Tanure if (!bus)
122f231ff38SLucas Tanure return ERR_PTR(-ENOMEM);
123f231ff38SLucas Tanure
124f5723cfcSCristian Ciocaltea max_msg_size = spi_max_message_size(spi);
125*4c114514SAndy Shevchenko reg_reserve_size = (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
126f5723cfcSCristian Ciocaltea if (max_size + reg_reserve_size > max_msg_size)
127f5723cfcSCristian Ciocaltea max_size -= reg_reserve_size;
128f5723cfcSCristian Ciocaltea
129f231ff38SLucas Tanure bus->free_on_exit = true;
130f231ff38SLucas Tanure bus->max_raw_read = max_size;
131f231ff38SLucas Tanure bus->max_raw_write = max_size;
132f5723cfcSCristian Ciocaltea
133f231ff38SLucas Tanure return bus;
134f231ff38SLucas Tanure }
135f231ff38SLucas Tanure
136f231ff38SLucas Tanure return ®map_spi;
137f231ff38SLucas Tanure }
138f231ff38SLucas Tanure
__regmap_init_spi(struct spi_device * spi,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)1393cfe7a74SNicolas Boichat struct regmap *__regmap_init_spi(struct spi_device *spi,
1403cfe7a74SNicolas Boichat const struct regmap_config *config,
1413cfe7a74SNicolas Boichat struct lock_class_key *lock_key,
1423cfe7a74SNicolas Boichat const char *lock_name)
143a676f083SMark Brown {
144f231ff38SLucas Tanure const struct regmap_bus *bus = regmap_get_spi_bus(spi, config);
145f231ff38SLucas Tanure
146f231ff38SLucas Tanure if (IS_ERR(bus))
147f231ff38SLucas Tanure return ERR_CAST(bus);
148f231ff38SLucas Tanure
149f231ff38SLucas Tanure return __regmap_init(&spi->dev, bus, &spi->dev, config, lock_key, lock_name);
150a676f083SMark Brown }
1513cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__regmap_init_spi);
152b33f9cbdSStephen Warren
__devm_regmap_init_spi(struct spi_device * spi,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)1533cfe7a74SNicolas Boichat struct regmap *__devm_regmap_init_spi(struct spi_device *spi,
1543cfe7a74SNicolas Boichat const struct regmap_config *config,
1553cfe7a74SNicolas Boichat struct lock_class_key *lock_key,
1563cfe7a74SNicolas Boichat const char *lock_name)
157c0eb4676SMark Brown {
158f231ff38SLucas Tanure const struct regmap_bus *bus = regmap_get_spi_bus(spi, config);
159f231ff38SLucas Tanure
160f231ff38SLucas Tanure if (IS_ERR(bus))
161f231ff38SLucas Tanure return ERR_CAST(bus);
162f231ff38SLucas Tanure
163f231ff38SLucas Tanure return __devm_regmap_init(&spi->dev, bus, &spi->dev, config, lock_key, lock_name);
164c0eb4676SMark Brown }
1653cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__devm_regmap_init_spi);
166c0eb4676SMark Brown
167b33f9cbdSStephen Warren MODULE_LICENSE("GPL");
168