162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Register map access API - SCCB support 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/i2c.h> 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/regmap.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "internal.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/** 1162306a36Sopenharmony_ci * sccb_is_available - Check if the adapter supports SCCB protocol 1262306a36Sopenharmony_ci * @adap: I2C adapter 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Return true if the I2C adapter is capable of using SCCB helper functions, 1562306a36Sopenharmony_ci * false otherwise. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_cistatic bool sccb_is_available(struct i2c_adapter *adap) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci u32 needed_funcs = I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci /* 2262306a36Sopenharmony_ci * If we ever want support for hardware doing SCCB natively, we will 2362306a36Sopenharmony_ci * introduce a sccb_xfer() callback to struct i2c_algorithm and check 2462306a36Sopenharmony_ci * for it here. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci return (i2c_get_functionality(adap) & needed_funcs) == needed_funcs; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * regmap_sccb_read - Read data from SCCB slave device 3262306a36Sopenharmony_ci * @context: Device that will be interacted with 3362306a36Sopenharmony_ci * @reg: Register to be read from 3462306a36Sopenharmony_ci * @val: Pointer to store read value 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * This executes the 2-phase write transmission cycle that is followed by a 3762306a36Sopenharmony_ci * 2-phase read transmission cycle, returning negative errno else zero on 3862306a36Sopenharmony_ci * success. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic int regmap_sccb_read(void *context, unsigned int reg, unsigned int *val) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct device *dev = context; 4362306a36Sopenharmony_ci struct i2c_client *i2c = to_i2c_client(dev); 4462306a36Sopenharmony_ci int ret; 4562306a36Sopenharmony_ci union i2c_smbus_data data; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags, 5062306a36Sopenharmony_ci I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE, NULL); 5162306a36Sopenharmony_ci if (ret < 0) 5262306a36Sopenharmony_ci goto out; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags, 5562306a36Sopenharmony_ci I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data); 5662306a36Sopenharmony_ci if (ret < 0) 5762306a36Sopenharmony_ci goto out; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci *val = data.byte; 6062306a36Sopenharmony_ciout: 6162306a36Sopenharmony_ci i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/** 6762306a36Sopenharmony_ci * regmap_sccb_write - Write data to SCCB slave device 6862306a36Sopenharmony_ci * @context: Device that will be interacted with 6962306a36Sopenharmony_ci * @reg: Register to write to 7062306a36Sopenharmony_ci * @val: Value to be written 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * This executes the SCCB 3-phase write transmission cycle, returning negative 7362306a36Sopenharmony_ci * errno else zero on success. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic int regmap_sccb_write(void *context, unsigned int reg, unsigned int val) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct device *dev = context; 7862306a36Sopenharmony_ci struct i2c_client *i2c = to_i2c_client(dev); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return i2c_smbus_write_byte_data(i2c, reg, val); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct regmap_bus regmap_sccb_bus = { 8462306a36Sopenharmony_ci .reg_write = regmap_sccb_write, 8562306a36Sopenharmony_ci .reg_read = regmap_sccb_read, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct regmap_bus *regmap_get_sccb_bus(struct i2c_client *i2c, 8962306a36Sopenharmony_ci const struct regmap_config *config) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci if (config->val_bits == 8 && config->reg_bits == 8 && 9262306a36Sopenharmony_ci sccb_is_available(i2c->adapter)) 9362306a36Sopenharmony_ci return ®map_sccb_bus; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return ERR_PTR(-ENOTSUPP); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct regmap *__regmap_init_sccb(struct i2c_client *i2c, 9962306a36Sopenharmony_ci const struct regmap_config *config, 10062306a36Sopenharmony_ci struct lock_class_key *lock_key, 10162306a36Sopenharmony_ci const char *lock_name) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci const struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (IS_ERR(bus)) 10662306a36Sopenharmony_ci return ERR_CAST(bus); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return __regmap_init(&i2c->dev, bus, &i2c->dev, config, 10962306a36Sopenharmony_ci lock_key, lock_name); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__regmap_init_sccb); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c, 11462306a36Sopenharmony_ci const struct regmap_config *config, 11562306a36Sopenharmony_ci struct lock_class_key *lock_key, 11662306a36Sopenharmony_ci const char *lock_name) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci const struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (IS_ERR(bus)) 12162306a36Sopenharmony_ci return ERR_CAST(bus); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config, 12462306a36Sopenharmony_ci lock_key, lock_name); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_regmap_init_sccb); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 129