From 0f614d834bccd3537881c5d0933803b407ce3283 Mon Sep 17 00:00:00 2001 From: Jean-Michel Hautbois Date: Sun, 31 Jan 2016 16:33:00 +0100 Subject: i2c: Add generic support passing secondary devices addresses Some I2C devices have multiple addresses assigned, for example each address corresponding to a different internal register map page of the device. So far drivers which need support for this have handled this with a driver specific and non-generic implementation, e.g. passing the additional address via platform data. This patch provides a new helper function called i2c_new_secondary_device() which is intended to provide a generic way to get the secondary address as well as instantiate a struct i2c_client for the secondary address. The function expects a pointer to the primary i2c_client, a name for the secondary address and an optional default address. The name is used as a handle to specify which secondary address to get. The default address is used as a fallback in case no secondary address was explicitly specified. In case no secondary address and no default address were specified the function returns NULL. For now the function only supports look-up of the secondary address from devicetree, but it can be extended in the future to for example support board files and/or ACPI. Signed-off-by: Jean-Michel Hautbois Acked-by: Rob Herring Acked-by: Mika Westerberg Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 96a25ae14494..6df7bad8c26c 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -349,6 +349,11 @@ extern int i2c_probe_func_quick_read(struct i2c_adapter *, unsigned short addr); extern struct i2c_client * i2c_new_dummy(struct i2c_adapter *adap, u16 address); +extern struct i2c_client * +i2c_new_secondary_device(struct i2c_client *client, + const char *name, + u16 default_addr); + extern void i2c_unregister_device(struct i2c_client *); #endif /* I2C */ -- cgit v1.2.3 From b4f210541fc319bd643ad9a4fdbfe2ce31be6cfc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 9 Jun 2016 16:53:47 +0200 Subject: i2c: add a protocol parameter to the alert callback .alert() is meant to be generic, but there is currently no way for the device driver to know which protocol generated the alert. Add a parameter in .alert() to help the device driver to understand what is given in data. This patch is required to have the support of SMBus Host Notify protocol through .alert(). Tested-by: Andrew Duggan For hwmon: Acked-by: Guenter Roeck For IPMI: Acked-by: Corey Minyard Signed-off-by: Benjamin Tissoires Signed-off-by: Wolfram Sang --- drivers/char/ipmi/ipmi_ssif.c | 6 +++++- drivers/hwmon/lm90.c | 6 +++++- drivers/i2c/i2c-smbus.c | 3 ++- include/linux/i2c.h | 7 ++++++- 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 097c86898608..5673ffff00be 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -568,12 +568,16 @@ static void retry_timeout(unsigned long data) } -static void ssif_alert(struct i2c_client *client, unsigned int data) +static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type, + unsigned int data) { struct ssif_info *ssif_info = i2c_get_clientdata(client); unsigned long oflags, *flags; bool do_get = false; + if (type != I2C_PROTOCOL_SMBUS_ALERT) + return; + ssif_inc_stat(ssif_info, alerts); flags = ipmi_ssif_lock_cond(ssif_info, &oflags); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index e30a5939dc0d..5b62c57e8bfc 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1624,10 +1624,14 @@ static int lm90_remove(struct i2c_client *client) return 0; } -static void lm90_alert(struct i2c_client *client, unsigned int flag) +static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, + unsigned int flag) { u16 alarms; + if (type != I2C_PROTOCOL_SMBUS_ALERT) + return; + if (lm90_is_tripped(client, &alarms)) { /* * Disable ALERT# output, because these chips don't implement diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index abb55d3e76f3..3b6765a4ebe9 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -56,7 +56,8 @@ static int smbus_do_alert(struct device *dev, void *addrp) if (client->dev.driver) { driver = to_i2c_driver(client->dev.driver); if (driver->alert) - driver->alert(client, data->flag); + driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, + data->flag); else dev_warn(&client->dev, "no driver alert()!\n"); } else diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 6df7bad8c26c..37a45dcd6592 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -126,6 +126,10 @@ i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, u8 command, u8 length, u8 *values); #endif /* I2C */ +enum i2c_alert_protocol { + I2C_PROTOCOL_SMBUS_ALERT, +}; + /** * struct i2c_driver - represent an I2C device driver * @class: What kind of i2c device we instantiate (for detect) @@ -181,7 +185,8 @@ struct i2c_driver { * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ - void (*alert)(struct i2c_client *, unsigned int data); + void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, + unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. -- cgit v1.2.3 From e456cd37bc28abe47dc65189df916ac0510ac1d4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 9 Jun 2016 16:53:48 +0200 Subject: i2c: smbus: add SMBus Host Notify support SMBus Host Notify allows a slave device to act as a master on a bus to notify the host of an interrupt. On Intel chipsets, the functionality is directly implemented in the firmware. We just need to export a function to call .alert() on the proper device driver. i2c_handle_smbus_host_notify() behaves like i2c_handle_smbus_alert(). When called, it schedules a task that will be able to sleep to go through the list of devices attached to the adapter. The current implementation allows one Host Notification to be scheduled while an other is running. Tested-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Signed-off-by: Wolfram Sang --- Documentation/i2c/smbus-protocol | 6 +++ drivers/i2c/i2c-smbus.c | 113 +++++++++++++++++++++++++++++++++++++-- include/linux/i2c-smbus.h | 44 +++++++++++++++ include/linux/i2c.h | 3 ++ include/uapi/linux/i2c.h | 1 + 5 files changed, 162 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol index 6012b12b3510..14d4ec1be245 100644 --- a/Documentation/i2c/smbus-protocol +++ b/Documentation/i2c/smbus-protocol @@ -199,6 +199,12 @@ alerting device's address. [S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P] +This is implemented in the following way in the Linux kernel: +* I2C bus drivers which support SMBus Host Notify should call + i2c_setup_smbus_host_notify() to setup SMBus Host Notify support. +* I2C drivers for devices which can trigger SMBus Host Notify should implement + the optional alert() callback. + Packet Error Checking (PEC) =========================== diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 3b6765a4ebe9..f574995b41c1 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -33,7 +33,8 @@ struct i2c_smbus_alert { struct alert_data { unsigned short addr; - u8 flag:1; + enum i2c_alert_protocol type; + unsigned int data; }; /* If this is the alerting device, notify its driver */ @@ -56,8 +57,7 @@ static int smbus_do_alert(struct device *dev, void *addrp) if (client->dev.driver) { driver = to_i2c_driver(client->dev.driver); if (driver->alert) - driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, - data->flag); + driver->alert(client, data->type, data->data); else dev_warn(&client->dev, "no driver alert()!\n"); } else @@ -97,8 +97,9 @@ static void smbus_alert(struct work_struct *work) if (status < 0) break; - data.flag = status & 1; + data.data = status & 1; data.addr = status >> 1; + data.type = I2C_PROTOCOL_SMBUS_ALERT; if (data.addr == prev_addr) { dev_warn(&ara->dev, "Duplicate SMBALERT# from dev " @@ -106,7 +107,7 @@ static void smbus_alert(struct work_struct *work) break; } dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", - data.addr, data.flag); + data.addr, data.data); /* Notify driver for the device which issued the alert */ device_for_each_child(&ara->adapter->dev, &data, @@ -240,6 +241,108 @@ int i2c_handle_smbus_alert(struct i2c_client *ara) } EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); +static void smbus_host_notify_work(struct work_struct *work) +{ + struct alert_data alert; + struct i2c_adapter *adapter; + unsigned long flags; + u16 payload; + u8 addr; + struct smbus_host_notify *data; + + data = container_of(work, struct smbus_host_notify, work); + + spin_lock_irqsave(&data->lock, flags); + payload = data->payload; + addr = data->addr; + adapter = data->adapter; + + /* clear the pending bit and release the spinlock */ + data->pending = false; + spin_unlock_irqrestore(&data->lock, flags); + + if (!adapter || !addr) + return; + + alert.type = I2C_PROTOCOL_SMBUS_HOST_NOTIFY; + alert.addr = addr; + alert.data = payload; + + device_for_each_child(&adapter->dev, &alert, smbus_do_alert); +} + +/** + * i2c_setup_smbus_host_notify - Allocate a new smbus_host_notify for the given + * I2C adapter. + * @adapter: the adapter we want to associate a Host Notify function + * + * Returns a struct smbus_host_notify pointer on success, and NULL on failure. + * The resulting smbus_host_notify must not be freed afterwards, it is a + * managed resource already. + */ +struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap) +{ + struct smbus_host_notify *host_notify; + + host_notify = devm_kzalloc(&adap->dev, sizeof(struct smbus_host_notify), + GFP_KERNEL); + if (!host_notify) + return NULL; + + host_notify->adapter = adap; + + spin_lock_init(&host_notify->lock); + INIT_WORK(&host_notify->work, smbus_host_notify_work); + + return host_notify; +} +EXPORT_SYMBOL_GPL(i2c_setup_smbus_host_notify); + +/** + * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct + * I2C client. + * @host_notify: the struct host_notify attached to the relevant adapter + * @data: the Host Notify data which contains the payload and address of the + * client + * Context: can't sleep + * + * Helper function to be called from an I2C bus driver's interrupt + * handler. It will schedule the Host Notify work, in turn calling the + * corresponding I2C device driver's alert function. + * + * host_notify should be a valid pointer previously returned by + * i2c_setup_smbus_host_notify(). + */ +int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify, + unsigned short addr, unsigned int data) +{ + unsigned long flags; + struct i2c_adapter *adapter; + + if (!host_notify || !host_notify->adapter) + return -EINVAL; + + adapter = host_notify->adapter; + + spin_lock_irqsave(&host_notify->lock, flags); + + if (host_notify->pending) { + spin_unlock_irqrestore(&host_notify->lock, flags); + dev_warn(&adapter->dev, "Host Notify already scheduled.\n"); + return -EBUSY; + } + + host_notify->payload = data; + host_notify->addr = addr; + + /* Mark that there is a pending notification and release the lock */ + host_notify->pending = true; + spin_unlock_irqrestore(&host_notify->lock, flags); + + return schedule_work(&host_notify->work); +} +EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); + module_i2c_driver(smbalert_driver); MODULE_AUTHOR("Jean Delvare "); diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h index 8f1b086ca5bc..4ac95bbe53ef 100644 --- a/include/linux/i2c-smbus.h +++ b/include/linux/i2c-smbus.h @@ -23,6 +23,8 @@ #define _LINUX_I2C_SMBUS_H #include +#include +#include /** @@ -48,4 +50,46 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, struct i2c_smbus_alert_setup *setup); int i2c_handle_smbus_alert(struct i2c_client *ara); +/** + * smbus_host_notify - internal structure used by the Host Notify mechanism. + * @adapter: the I2C adapter associated with this struct + * @work: worker used to schedule the IRQ in the slave device + * @lock: spinlock to check if a notification is already pending + * @pending: flag set when a notification is pending (any new notification will + * be rejected if pending is true) + * @payload: the actual payload of the Host Notify event + * @addr: the address of the slave device which raised the notification + * + * This struct needs to be allocated by i2c_setup_smbus_host_notify() and does + * not need to be freed. Internally, i2c_setup_smbus_host_notify() uses a + * managed resource to clean this up when the adapter get released. + */ +struct smbus_host_notify { + struct i2c_adapter *adapter; + struct work_struct work; + spinlock_t lock; + bool pending; + u16 payload; + u8 addr; +}; + +#if IS_ENABLED(CONFIG_I2C_SMBUS) +struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap); +int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify, + unsigned short addr, unsigned int data); +#else +static inline struct smbus_host_notify * +i2c_setup_smbus_host_notify(struct i2c_adapter *adap) +{ + return NULL; +} + +static inline int +i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify, + unsigned short addr, unsigned int data) +{ + return 0; +} +#endif /* I2C_SMBUS */ + #endif /* _LINUX_I2C_SMBUS_H */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 37a45dcd6592..fffdc270ca18 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -128,6 +128,7 @@ i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, enum i2c_alert_protocol { I2C_PROTOCOL_SMBUS_ALERT, + I2C_PROTOCOL_SMBUS_HOST_NOTIFY, }; /** @@ -184,6 +185,8 @@ struct i2c_driver { * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). + * For the SMBus Host Notify protocol, the data corresponds to the + * 16-bit payload data reported by the slave device acting as master. */ void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, unsigned int data); diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h index adcbef4bff61..009e27bb9abe 100644 --- a/include/uapi/linux/i2c.h +++ b/include/uapi/linux/i2c.h @@ -102,6 +102,7 @@ struct i2c_msg { #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 #define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ +#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 #define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ I2C_FUNC_SMBUS_WRITE_BYTE) -- cgit v1.2.3 From 55d38d060e999ca1a3ea6eb132105a0301e4cd04 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 6 Jun 2016 14:26:22 +0300 Subject: i2c: core: Add function for finding the bus speed from ACPI ACPI 5 specification doesn't have property for the I2C bus speed but I2cSerialBus resource descriptors which define each controller-slave connection define the maximum speed supported by that connection. Thus finding the maximum safe speed for the bus is to walk all I2cSerialBus resources that are associated to I2C controller and use the speed of slowest connection. Add function i2c_acpi_find_bus_speed() to the i2c-core that adapter drivers can call prior registering itself to core. This implies two-step walk through the I2cSerialBus resources: call to i2c_acpi_find_bus_speed() does the first scan and finds the safe bus speed that adapter drivers can set up. Adapter driver registration does the second scan when i2c-core creates the I2C slaves by calling the i2c_acpi_register_devices(). In that way the bus speed is set in case slave device probe gets called during registration and does communication. Implement this by reusing the existing ACPI I2C walk routines in the i2c-core. Extend them so that slowest connection speed is saved during the walk and I2C slaves are registered only when calling through the i2c_acpi_register_devices() with the i2c_adapter pointer. Signed-off-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 74 ++++++++++++++++++++++++++++++++++++++++---------- include/linux/i2c.h | 9 ++++++ 2 files changed, 68 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index abe41369eec1..347494a8750e 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -105,11 +105,13 @@ struct gsb_buffer { struct i2c_acpi_lookup { struct i2c_board_info *info; + struct i2c_adapter *adapter; /* set only when registering slaves */ acpi_handle adapter_handle; acpi_handle device_handle; + u32 min_speed; }; -static int i2c_acpi_find_address(struct acpi_resource *ares, void *data) +static int i2c_acpi_find_resource(struct acpi_resource *ares, void *data) { struct i2c_acpi_lookup *lookup = data; struct i2c_board_info *info = lookup->info; @@ -135,17 +137,20 @@ static int i2c_acpi_find_address(struct acpi_resource *ares, void *data) info->addr = sb->slave_address; if (sb->access_mode == ACPI_I2C_10BIT_MODE) info->flags |= I2C_CLIENT_TEN; + /* Save speed of the slowest device */ + if (sb->connection_speed < lookup->min_speed) + lookup->min_speed = sb->connection_speed; } return 1; } -static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, - void *data, void **return_value) +static acpi_status i2c_acpi_slave_lookup(acpi_handle handle, u32 level, + void *data, void **return_value) { - struct i2c_adapter *adapter = data; + struct i2c_acpi_lookup *lookup = data; + struct i2c_adapter *adapter = lookup->adapter; struct list_head resource_list; - struct i2c_acpi_lookup lookup; struct resource_entry *entry; struct i2c_board_info info; struct acpi_device *adev; @@ -159,10 +164,8 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, memset(&info, 0, sizeof(info)); info.fwnode = acpi_fwnode_handle(adev); - memset(&lookup, 0, sizeof(lookup)); - lookup.adapter_handle = ACPI_HANDLE(&adapter->dev); - lookup.device_handle = handle; - lookup.info = &info; + lookup->device_handle = handle; + lookup->info = &info; /* * Look up for I2cSerialBus resource with ResourceSource that @@ -170,10 +173,10 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, */ INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, - i2c_acpi_find_address, &lookup); + i2c_acpi_find_resource, lookup); acpi_dev_free_resource_list(&resource_list); - if (ret < 0 || !info.addr) + if (ret < 0 || !info.addr || !lookup->adapter) return AE_OK; /* Then fill IRQ number if any */ @@ -204,6 +207,14 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, #define I2C_ACPI_MAX_SCAN_DEPTH 32 +static acpi_status i2c_acpi_walk(struct i2c_acpi_lookup *lookup) +{ + return acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_slave_lookup, NULL, + lookup, NULL); +} + /** * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter * @adap: pointer to adapter @@ -214,19 +225,52 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, */ static void i2c_acpi_register_devices(struct i2c_adapter *adap) { + struct i2c_acpi_lookup lookup; acpi_status status; if (!has_acpi_companion(&adap->dev)) return; - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - I2C_ACPI_MAX_SCAN_DEPTH, - i2c_acpi_add_device, NULL, - adap, NULL); + memset(&lookup, 0, sizeof(lookup)); + lookup.adapter = adap; + lookup.adapter_handle = ACPI_HANDLE(&adap->dev); + + status = i2c_acpi_walk(&lookup); if (ACPI_FAILURE(status)) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } +/** + * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI + * @dev: The device owning the bus + * + * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves + * devices connected to this bus and use the speed of slowest device. + * + * Returns the speed in Hz or zero + */ +u32 i2c_acpi_find_bus_speed(struct device *dev) +{ + struct i2c_acpi_lookup lookup; + acpi_status status; + + if (!has_acpi_companion(dev)) + return 0; + + memset(&lookup, 0, sizeof(lookup)); + lookup.adapter_handle = ACPI_HANDLE(dev); + lookup.min_speed = UINT_MAX; + + status = i2c_acpi_walk(&lookup); + if (ACPI_FAILURE(status)) { + dev_warn(dev, "unable to find I2C bus speed from ACPI\n"); + return 0; + } + + return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0; +} +EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); + #else /* CONFIG_ACPI */ static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } #endif /* CONFIG_ACPI */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index fffdc270ca18..5cde08719fb6 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -766,4 +766,13 @@ static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ +#if IS_ENABLED(CONFIG_ACPI) +u32 i2c_acpi_find_bus_speed(struct device *dev); +#else +static inline u32 i2c_acpi_find_bus_speed(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_ACPI */ + #endif /* _LINUX_I2C_H */ -- cgit v1.2.3 From a7284a82e826b88eb6264747cee57888d4076c48 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 6 Jun 2016 10:48:50 +0200 Subject: eeprom: at24: platform_data: use BIT() macro Use BIT() macro to replace the 0xXX constants in platform_data flags definitions. Signed-off-by: Bartosz Golaszewski Signed-off-by: Wolfram Sang --- include/linux/platform_data/at24.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/platform_data/at24.h b/include/linux/platform_data/at24.h index be830b141d83..fcfdfe3c0e9c 100644 --- a/include/linux/platform_data/at24.h +++ b/include/linux/platform_data/at24.h @@ -10,6 +10,7 @@ #include #include +#include /** * struct at24_platform_data - data to set up at24 (generic eeprom) driver @@ -43,10 +44,10 @@ struct at24_platform_data { u32 byte_len; /* size (sum of all addr) */ u16 page_size; /* for writes */ u8 flags; -#define AT24_FLAG_ADDR16 0x80 /* address pointer is 16 bit */ -#define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */ -#define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */ -#define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */ +#define AT24_FLAG_ADDR16 BIT(7) /* address pointer is 16 bit */ +#define AT24_FLAG_READONLY BIT(6) /* sysfs-entry will be read-only */ +#define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */ +#define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */ void (*setup)(struct nvmem_device *nvmem, void *context); void *context; -- cgit v1.2.3 From 818d0220d857cc92cb37600758c5b47c3df3782b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 6 Jun 2016 10:48:51 +0200 Subject: eeprom: at24: support reading the serial number for 24csxx The chips from the at24cs family have two memory areas - a regular read-write block and a read-only area containing the serial number. The latter is visible on a different slave address (the address of the rw memory block + 0x08). In order to access both blocks the user needs to instantiate a regular at24c device for the rw block address and a corresponding at24cs device on the serial number block address. Add a function that allows to access the serial number and assign it to at24->read_func if the chip allows serial number read operations and the driver was passed the relevant flag for this device. Signed-off-by: Bartosz Golaszewski Signed-off-by: Wolfram Sang --- drivers/misc/eeprom/at24.c | 80 +++++++++++++++++++++++++++++++++++++- include/linux/platform_data/at24.h | 1 + 2 files changed, 79 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 6acf35af271c..3b27ff14876c 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -134,16 +134,34 @@ static const struct i2c_device_id at24_ids[] = { { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, /* old variants can't be handled with this generic entry! */ { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, + { "24cs01", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, + { "24cs02", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, /* spd is a 24c02 in memory DIMMs */ { "spd", AT24_DEVICE_MAGIC(2048 / 8, AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, + { "24cs04", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, /* 24rf08 quirk is handled at i2c-core */ { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { "24cs08", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, + { "24cs16", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, + { "24cs32", AT24_DEVICE_MAGIC(16, + AT24_FLAG_ADDR16 | + AT24_FLAG_SERIAL | + AT24_FLAG_READONLY) }, { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + { "24cs64", AT24_DEVICE_MAGIC(16, + AT24_FLAG_ADDR16 | + AT24_FLAG_SERIAL | + AT24_FLAG_READONLY) }, { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, @@ -276,6 +294,59 @@ static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf, return -ETIMEDOUT; } +static ssize_t at24_eeprom_read_serial(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + u8 addrbuf[2]; + int status; + + client = at24_translate_offset(at24, &offset); + + memset(msg, 0, sizeof(msg)); + msg[0].addr = client->addr; + msg[0].buf = addrbuf; + + /* + * The address pointer of the device is shared between the regular + * EEPROM array and the serial number block. The dummy write (part of + * the sequential read protocol) ensures the address pointer is reset + * to the desired position. + */ + if (at24->chip.flags & AT24_FLAG_ADDR16) { + /* + * For 16 bit address pointers, the word address must contain + * a '10' sequence in bits 11 and 10 regardless of the + * intended position of the address pointer. + */ + addrbuf[0] = 0x08; + addrbuf[1] = offset; + msg[0].len = 2; + } else { + /* + * Otherwise the word address must begin with a '10' sequence, + * regardless of the intended address. + */ + addrbuf[0] = 0x80 + offset; + msg[0].len = 1; + } + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + return count; + } + + return -ETIMEDOUT; +} + /* * Note that if the hardware write-protect pin is pulled high, the whole * chip is normally write protected. But there are plenty of product @@ -577,8 +648,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->chip = chip; at24->num_addresses = num_addresses; - at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus - : at24_eeprom_read_i2c; + if (chip.flags & AT24_FLAG_SERIAL) { + at24->read_func = at24_eeprom_read_serial; + } else { + at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus + : at24_eeprom_read_i2c; + } + if (at24->use_smbus) { if (at24->use_smbus_write == I2C_SMBUS_I2C_BLOCK_DATA) at24->write_func = at24_eeprom_write_smbus_block; diff --git a/include/linux/platform_data/at24.h b/include/linux/platform_data/at24.h index fcfdfe3c0e9c..d59587473aed 100644 --- a/include/linux/platform_data/at24.h +++ b/include/linux/platform_data/at24.h @@ -48,6 +48,7 @@ struct at24_platform_data { #define AT24_FLAG_READONLY BIT(6) /* sysfs-entry will be read-only */ #define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */ #define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */ +#define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */ void (*setup)(struct nvmem_device *nvmem, void *context); void *context; -- cgit v1.2.3 From 0b813658c11532be90cbf5f579a8ba45a8cc9dbf Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 6 Jun 2016 10:48:54 +0200 Subject: eeprom: at24: add support for at24mac series Add a new read function to the at24 driver allowing to retrieve the factory-programmed mac address embedded in chips from the at24mac family. These chips can be instantiated similarily to the at24cs family, except that there's no way of having access to both the serial number and the mac address at the same time - the user must instantiate either an at24cs or at24mac device as both special memory areas are accessible on the same slave address. Signed-off-by: Bartosz Golaszewski Signed-off-by: Wolfram Sang --- drivers/misc/eeprom/at24.c | 42 ++++++++++++++++++++++++++++++++++++++ include/linux/platform_data/at24.h | 1 + 2 files changed, 43 insertions(+) (limited to 'include/linux') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 3b27ff14876c..04e91e331fc5 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -139,6 +139,10 @@ static const struct i2c_device_id at24_ids[] = { { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, { "24cs02", AT24_DEVICE_MAGIC(16, AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24mac402", AT24_DEVICE_MAGIC(48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY) }, + { "24mac602", AT24_DEVICE_MAGIC(64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY) }, /* spd is a 24c02 in memory DIMMs */ { "spd", AT24_DEVICE_MAGIC(2048 / 8, AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, @@ -347,6 +351,36 @@ static ssize_t at24_eeprom_read_serial(struct at24_data *at24, char *buf, return -ETIMEDOUT; } +static ssize_t at24_eeprom_read_mac(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + u8 addrbuf[2]; + int status; + + client = at24_translate_offset(at24, &offset); + + memset(msg, 0, sizeof(msg)); + msg[0].addr = client->addr; + msg[0].buf = addrbuf; + addrbuf[0] = 0x90 + offset; + msg[0].len = 1; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + return count; + } + + return -ETIMEDOUT; +} + /* * Note that if the hardware write-protect pin is pulled high, the whole * chip is normally write protected. But there are plenty of product @@ -648,8 +682,16 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->chip = chip; at24->num_addresses = num_addresses; + if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) { + dev_err(&client->dev, + "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); + return -EINVAL; + } + if (chip.flags & AT24_FLAG_SERIAL) { at24->read_func = at24_eeprom_read_serial; + } else if (chip.flags & AT24_FLAG_MAC) { + at24->read_func = at24_eeprom_read_mac; } else { at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus : at24_eeprom_read_i2c; diff --git a/include/linux/platform_data/at24.h b/include/linux/platform_data/at24.h index d59587473aed..271a4e25af67 100644 --- a/include/linux/platform_data/at24.h +++ b/include/linux/platform_data/at24.h @@ -49,6 +49,7 @@ struct at24_platform_data { #define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */ #define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */ #define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */ +#define AT24_FLAG_MAC BIT(2) /* factory-programmed mac address */ void (*setup)(struct nvmem_device *nvmem, void *context); void *context; -- cgit v1.2.3 From 38d0fc4662474c6219166505d0a68b45a583fcfb Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 19 Jul 2016 05:54:56 +0200 Subject: Revert "i2c: core: Add function for finding the bus speed from ACPI" This reverts commit 55d38d060e999ca1a3ea6eb132105a0301e4cd04. There were too heavy merge conflicts and the driver code making use of this was not ready yet anyhow. So, we wait one cycle. Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 74 ++++++++++---------------------------------------- include/linux/i2c.h | 9 ------ 2 files changed, 15 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 347494a8750e..abe41369eec1 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -105,13 +105,11 @@ struct gsb_buffer { struct i2c_acpi_lookup { struct i2c_board_info *info; - struct i2c_adapter *adapter; /* set only when registering slaves */ acpi_handle adapter_handle; acpi_handle device_handle; - u32 min_speed; }; -static int i2c_acpi_find_resource(struct acpi_resource *ares, void *data) +static int i2c_acpi_find_address(struct acpi_resource *ares, void *data) { struct i2c_acpi_lookup *lookup = data; struct i2c_board_info *info = lookup->info; @@ -137,20 +135,17 @@ static int i2c_acpi_find_resource(struct acpi_resource *ares, void *data) info->addr = sb->slave_address; if (sb->access_mode == ACPI_I2C_10BIT_MODE) info->flags |= I2C_CLIENT_TEN; - /* Save speed of the slowest device */ - if (sb->connection_speed < lookup->min_speed) - lookup->min_speed = sb->connection_speed; } return 1; } -static acpi_status i2c_acpi_slave_lookup(acpi_handle handle, u32 level, - void *data, void **return_value) +static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) { - struct i2c_acpi_lookup *lookup = data; - struct i2c_adapter *adapter = lookup->adapter; + struct i2c_adapter *adapter = data; struct list_head resource_list; + struct i2c_acpi_lookup lookup; struct resource_entry *entry; struct i2c_board_info info; struct acpi_device *adev; @@ -164,8 +159,10 @@ static acpi_status i2c_acpi_slave_lookup(acpi_handle handle, u32 level, memset(&info, 0, sizeof(info)); info.fwnode = acpi_fwnode_handle(adev); - lookup->device_handle = handle; - lookup->info = &info; + memset(&lookup, 0, sizeof(lookup)); + lookup.adapter_handle = ACPI_HANDLE(&adapter->dev); + lookup.device_handle = handle; + lookup.info = &info; /* * Look up for I2cSerialBus resource with ResourceSource that @@ -173,10 +170,10 @@ static acpi_status i2c_acpi_slave_lookup(acpi_handle handle, u32 level, */ INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, - i2c_acpi_find_resource, lookup); + i2c_acpi_find_address, &lookup); acpi_dev_free_resource_list(&resource_list); - if (ret < 0 || !info.addr || !lookup->adapter) + if (ret < 0 || !info.addr) return AE_OK; /* Then fill IRQ number if any */ @@ -207,14 +204,6 @@ static acpi_status i2c_acpi_slave_lookup(acpi_handle handle, u32 level, #define I2C_ACPI_MAX_SCAN_DEPTH 32 -static acpi_status i2c_acpi_walk(struct i2c_acpi_lookup *lookup) -{ - return acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - I2C_ACPI_MAX_SCAN_DEPTH, - i2c_acpi_slave_lookup, NULL, - lookup, NULL); -} - /** * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter * @adap: pointer to adapter @@ -225,52 +214,19 @@ static acpi_status i2c_acpi_walk(struct i2c_acpi_lookup *lookup) */ static void i2c_acpi_register_devices(struct i2c_adapter *adap) { - struct i2c_acpi_lookup lookup; acpi_status status; if (!has_acpi_companion(&adap->dev)) return; - memset(&lookup, 0, sizeof(lookup)); - lookup.adapter = adap; - lookup.adapter_handle = ACPI_HANDLE(&adap->dev); - - status = i2c_acpi_walk(&lookup); + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_add_device, NULL, + adap, NULL); if (ACPI_FAILURE(status)) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } -/** - * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI - * @dev: The device owning the bus - * - * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves - * devices connected to this bus and use the speed of slowest device. - * - * Returns the speed in Hz or zero - */ -u32 i2c_acpi_find_bus_speed(struct device *dev) -{ - struct i2c_acpi_lookup lookup; - acpi_status status; - - if (!has_acpi_companion(dev)) - return 0; - - memset(&lookup, 0, sizeof(lookup)); - lookup.adapter_handle = ACPI_HANDLE(dev); - lookup.min_speed = UINT_MAX; - - status = i2c_acpi_walk(&lookup); - if (ACPI_FAILURE(status)) { - dev_warn(dev, "unable to find I2C bus speed from ACPI\n"); - return 0; - } - - return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0; -} -EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); - #else /* CONFIG_ACPI */ static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } #endif /* CONFIG_ACPI */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 5cde08719fb6..fffdc270ca18 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -766,13 +766,4 @@ static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ -#if IS_ENABLED(CONFIG_ACPI) -u32 i2c_acpi_find_bus_speed(struct device *dev); -#else -static inline u32 i2c_acpi_find_bus_speed(struct device *dev) -{ - return 0; -} -#endif /* CONFIG_ACPI */ - #endif /* _LINUX_I2C_H */ -- cgit v1.2.3 From 1ab0a1192dc8b02162c7ce03d332ef8e599e5f4e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 19 Jul 2016 13:15:49 +0200 Subject: i2c: i2c-smbus: drop useless stubs Drivers which use the SMBus extensions select I2C_SMBUS, so the stubs are not needed. Signed-off-by: Jean Delvare Reviewed-by: Benjamin Tissoires Signed-off-by: Wolfram Sang --- include/linux/i2c-smbus.h | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h index 4ac95bbe53ef..c2e3324f9468 100644 --- a/include/linux/i2c-smbus.h +++ b/include/linux/i2c-smbus.h @@ -73,23 +73,8 @@ struct smbus_host_notify { u8 addr; }; -#if IS_ENABLED(CONFIG_I2C_SMBUS) struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap); int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify, unsigned short addr, unsigned int data); -#else -static inline struct smbus_host_notify * -i2c_setup_smbus_host_notify(struct i2c_adapter *adap) -{ - return NULL; -} - -static inline int -i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify, - unsigned short addr, unsigned int data) -{ - return 0; -} -#endif /* I2C_SMBUS */ #endif /* _LINUX_I2C_SMBUS_H */ -- cgit v1.2.3