1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun // Register map access API - SCCB support
3*4882a593Smuzhiyun
4*4882a593Smuzhiyun #include <linux/i2c.h>
5*4882a593Smuzhiyun #include <linux/module.h>
6*4882a593Smuzhiyun #include <linux/regmap.h>
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include "internal.h"
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun /**
11*4882a593Smuzhiyun * sccb_is_available - Check if the adapter supports SCCB protocol
12*4882a593Smuzhiyun * @adap: I2C adapter
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * Return true if the I2C adapter is capable of using SCCB helper functions,
15*4882a593Smuzhiyun * false otherwise.
16*4882a593Smuzhiyun */
sccb_is_available(struct i2c_adapter * adap)17*4882a593Smuzhiyun static bool sccb_is_available(struct i2c_adapter *adap)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun u32 needed_funcs = I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /*
22*4882a593Smuzhiyun * If we ever want support for hardware doing SCCB natively, we will
23*4882a593Smuzhiyun * introduce a sccb_xfer() callback to struct i2c_algorithm and check
24*4882a593Smuzhiyun * for it here.
25*4882a593Smuzhiyun */
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun return (i2c_get_functionality(adap) & needed_funcs) == needed_funcs;
28*4882a593Smuzhiyun }
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /**
31*4882a593Smuzhiyun * regmap_sccb_read - Read data from SCCB slave device
32*4882a593Smuzhiyun * @context: Device that will be interacted with
33*4882a593Smuzhiyun * @reg: Register to be read from
34*4882a593Smuzhiyun * @val: Pointer to store read value
35*4882a593Smuzhiyun *
36*4882a593Smuzhiyun * This executes the 2-phase write transmission cycle that is followed by a
37*4882a593Smuzhiyun * 2-phase read transmission cycle, returning negative errno else zero on
38*4882a593Smuzhiyun * success.
39*4882a593Smuzhiyun */
regmap_sccb_read(void * context,unsigned int reg,unsigned int * val)40*4882a593Smuzhiyun static int regmap_sccb_read(void *context, unsigned int reg, unsigned int *val)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun struct device *dev = context;
43*4882a593Smuzhiyun struct i2c_client *i2c = to_i2c_client(dev);
44*4882a593Smuzhiyun int ret;
45*4882a593Smuzhiyun union i2c_smbus_data data;
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
50*4882a593Smuzhiyun I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE, NULL);
51*4882a593Smuzhiyun if (ret < 0)
52*4882a593Smuzhiyun goto out;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
55*4882a593Smuzhiyun I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
56*4882a593Smuzhiyun if (ret < 0)
57*4882a593Smuzhiyun goto out;
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun *val = data.byte;
60*4882a593Smuzhiyun out:
61*4882a593Smuzhiyun i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun return ret;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun /**
67*4882a593Smuzhiyun * regmap_sccb_write - Write data to SCCB slave device
68*4882a593Smuzhiyun * @context: Device that will be interacted with
69*4882a593Smuzhiyun * @reg: Register to write to
70*4882a593Smuzhiyun * @val: Value to be written
71*4882a593Smuzhiyun *
72*4882a593Smuzhiyun * This executes the SCCB 3-phase write transmission cycle, returning negative
73*4882a593Smuzhiyun * errno else zero on success.
74*4882a593Smuzhiyun */
regmap_sccb_write(void * context,unsigned int reg,unsigned int val)75*4882a593Smuzhiyun static int regmap_sccb_write(void *context, unsigned int reg, unsigned int val)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun struct device *dev = context;
78*4882a593Smuzhiyun struct i2c_client *i2c = to_i2c_client(dev);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun return i2c_smbus_write_byte_data(i2c, reg, val);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun static struct regmap_bus regmap_sccb_bus = {
84*4882a593Smuzhiyun .reg_write = regmap_sccb_write,
85*4882a593Smuzhiyun .reg_read = regmap_sccb_read,
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun
regmap_get_sccb_bus(struct i2c_client * i2c,const struct regmap_config * config)88*4882a593Smuzhiyun static const struct regmap_bus *regmap_get_sccb_bus(struct i2c_client *i2c,
89*4882a593Smuzhiyun const struct regmap_config *config)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun if (config->val_bits == 8 && config->reg_bits == 8 &&
92*4882a593Smuzhiyun sccb_is_available(i2c->adapter))
93*4882a593Smuzhiyun return ®map_sccb_bus;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun return ERR_PTR(-ENOTSUPP);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
__regmap_init_sccb(struct i2c_client * i2c,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)98*4882a593Smuzhiyun struct regmap *__regmap_init_sccb(struct i2c_client *i2c,
99*4882a593Smuzhiyun const struct regmap_config *config,
100*4882a593Smuzhiyun struct lock_class_key *lock_key,
101*4882a593Smuzhiyun const char *lock_name)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun const struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config);
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun if (IS_ERR(bus))
106*4882a593Smuzhiyun return ERR_CAST(bus);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
109*4882a593Smuzhiyun lock_key, lock_name);
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__regmap_init_sccb);
112*4882a593Smuzhiyun
__devm_regmap_init_sccb(struct i2c_client * i2c,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)113*4882a593Smuzhiyun struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c,
114*4882a593Smuzhiyun const struct regmap_config *config,
115*4882a593Smuzhiyun struct lock_class_key *lock_key,
116*4882a593Smuzhiyun const char *lock_name)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun const struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun if (IS_ERR(bus))
121*4882a593Smuzhiyun return ERR_CAST(bus);
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config,
124*4882a593Smuzhiyun lock_key, lock_name);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__devm_regmap_init_sccb);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
129