mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	i2c: core changes for slave support
Finally(!), make Linux support being an I2C slave. Most of the existing infrastructure is reused. We mainly add i2c_slave_register/unregister() calls which tells i2c bus drivers to activate the slave mode. Then, they also get a callback to report slave events to. Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
		
							parent
							
								
									40ed1b4caf
								
							
						
					
					
						commit
						4b1acc4333
					
				
					 2 changed files with 78 additions and 0 deletions
				
			
		| 
						 | 
					@ -24,6 +24,7 @@
 | 
				
			||||||
   (c) 2013  Wolfram Sang <wsa@the-dreams.de>
 | 
					   (c) 2013  Wolfram Sang <wsa@the-dreams.de>
 | 
				
			||||||
   I2C ACPI code Copyright (C) 2014 Intel Corp
 | 
					   I2C ACPI code Copyright (C) 2014 Intel Corp
 | 
				
			||||||
   Author: Lan Tianyu <tianyu.lan@intel.com>
 | 
					   Author: Lan Tianyu <tianyu.lan@intel.com>
 | 
				
			||||||
 | 
					   I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
| 
						 | 
					@ -2911,6 +2912,54 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(i2c_smbus_xfer);
 | 
					EXPORT_SYMBOL(i2c_smbus_xfer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!client || !slave_cb)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!(client->flags & I2C_CLIENT_TEN)) {
 | 
				
			||||||
 | 
							/* Enforce stricter address checking */
 | 
				
			||||||
 | 
							ret = i2c_check_addr_validity(client->addr);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!client->adapter->algo->reg_slave)
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client->slave_cb = slave_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i2c_lock_adapter(client->adapter);
 | 
				
			||||||
 | 
						ret = client->adapter->algo->reg_slave(client);
 | 
				
			||||||
 | 
						i2c_unlock_adapter(client->adapter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							client->slave_cb = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(i2c_slave_register);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int i2c_slave_unregister(struct i2c_client *client)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!client->adapter->algo->unreg_slave)
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i2c_lock_adapter(client->adapter);
 | 
				
			||||||
 | 
						ret = client->adapter->algo->unreg_slave(client);
 | 
				
			||||||
 | 
						i2c_unlock_adapter(client->adapter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ret == 0)
 | 
				
			||||||
 | 
							client->slave_cb = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(i2c_slave_unregister);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
 | 
					MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
 | 
				
			||||||
MODULE_DESCRIPTION("I2C-Bus main module");
 | 
					MODULE_DESCRIPTION("I2C-Bus main module");
 | 
				
			||||||
MODULE_LICENSE("GPL");
 | 
					MODULE_LICENSE("GPL");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,8 @@ struct i2c_client;
 | 
				
			||||||
struct i2c_driver;
 | 
					struct i2c_driver;
 | 
				
			||||||
union i2c_smbus_data;
 | 
					union i2c_smbus_data;
 | 
				
			||||||
struct i2c_board_info;
 | 
					struct i2c_board_info;
 | 
				
			||||||
 | 
					enum i2c_slave_event;
 | 
				
			||||||
 | 
					typedef int (*i2c_slave_cb_t)(struct i2c_client *, enum i2c_slave_event, u8 *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct module;
 | 
					struct module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -209,6 +211,8 @@ struct i2c_driver {
 | 
				
			||||||
 * @irq: indicates the IRQ generated by this device (if any)
 | 
					 * @irq: indicates the IRQ generated by this device (if any)
 | 
				
			||||||
 * @detected: member of an i2c_driver.clients list or i2c-core's
 | 
					 * @detected: member of an i2c_driver.clients list or i2c-core's
 | 
				
			||||||
 *	userspace_devices list
 | 
					 *	userspace_devices list
 | 
				
			||||||
 | 
					 * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
 | 
				
			||||||
 | 
					 *	calls it to pass on slave events to the slave driver.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * An i2c_client identifies a single device (i.e. chip) connected to an
 | 
					 * An i2c_client identifies a single device (i.e. chip) connected to an
 | 
				
			||||||
 * i2c bus. The behaviour exposed to Linux is defined by the driver
 | 
					 * i2c bus. The behaviour exposed to Linux is defined by the driver
 | 
				
			||||||
| 
						 | 
					@ -224,6 +228,7 @@ struct i2c_client {
 | 
				
			||||||
	struct device dev;		/* the device structure		*/
 | 
						struct device dev;		/* the device structure		*/
 | 
				
			||||||
	int irq;			/* irq issued by device		*/
 | 
						int irq;			/* irq issued by device		*/
 | 
				
			||||||
	struct list_head detected;
 | 
						struct list_head detected;
 | 
				
			||||||
 | 
						i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
 | 
					#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -246,6 +251,25 @@ static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
 | 
				
			||||||
	dev_set_drvdata(&dev->dev, data);
 | 
						dev_set_drvdata(&dev->dev, data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* I2C slave support */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum i2c_slave_event {
 | 
				
			||||||
 | 
						I2C_SLAVE_REQ_READ_START,
 | 
				
			||||||
 | 
						I2C_SLAVE_REQ_READ_END,
 | 
				
			||||||
 | 
						I2C_SLAVE_REQ_WRITE_START,
 | 
				
			||||||
 | 
						I2C_SLAVE_REQ_WRITE_END,
 | 
				
			||||||
 | 
						I2C_SLAVE_STOP,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
 | 
				
			||||||
 | 
					extern int i2c_slave_unregister(struct i2c_client *client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int i2c_slave_event(struct i2c_client *client,
 | 
				
			||||||
 | 
									  enum i2c_slave_event event, u8 *val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return client->slave_cb(client, event, val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct i2c_board_info - template for device creation
 | 
					 * struct i2c_board_info - template for device creation
 | 
				
			||||||
 * @type: chip type, to initialize i2c_client.name
 | 
					 * @type: chip type, to initialize i2c_client.name
 | 
				
			||||||
| 
						 | 
					@ -352,6 +376,8 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
 | 
				
			||||||
 *   into I2C transfers instead.
 | 
					 *   into I2C transfers instead.
 | 
				
			||||||
 * @functionality: Return the flags that this algorithm/adapter pair supports
 | 
					 * @functionality: Return the flags that this algorithm/adapter pair supports
 | 
				
			||||||
 *   from the I2C_FUNC_* flags.
 | 
					 *   from the I2C_FUNC_* flags.
 | 
				
			||||||
 | 
					 * @reg_slave: Register given client to I2C slave mode of this adapter
 | 
				
			||||||
 | 
					 * @unreg_slave: Unregister given client from I2C slave mode of this adapter
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * The following structs are for those who like to implement new bus drivers:
 | 
					 * The following structs are for those who like to implement new bus drivers:
 | 
				
			||||||
 * i2c_algorithm is the interface to a class of hardware solutions which can
 | 
					 * i2c_algorithm is the interface to a class of hardware solutions which can
 | 
				
			||||||
| 
						 | 
					@ -377,6 +403,9 @@ struct i2c_algorithm {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* To determine what the adapter supports */
 | 
						/* To determine what the adapter supports */
 | 
				
			||||||
	u32 (*functionality) (struct i2c_adapter *);
 | 
						u32 (*functionality) (struct i2c_adapter *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int (*reg_slave)(struct i2c_client *client);
 | 
				
			||||||
 | 
						int (*unreg_slave)(struct i2c_client *client);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue