summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2016-10-13 08:10:40 -0400
committerWolfram Sang <wsa@the-dreams.de>2016-11-24 10:22:06 -0500
commit4d5538f5882a6b67eefbab0f0a3a67ce811621aa (patch)
tree1a5e7055d2db7d178fd178c44740170eeec60228
parentc912a25a5a12a497bb47068e3d42d7c9b67bde12 (diff)
i2c: use an IRQ to report Host Notify events, not alert
The current SMBus Host Notify implementation relies on .alert() to relay its notifications. However, the use cases where SMBus Host Notify is needed currently is to signal data ready on touchpads. This is closer to an IRQ than a custom API through .alert(). Given that the 2 touchpad manufacturers (Synaptics and Elan) that use SMBus Host Notify don't put any data in the SMBus payload, the concept actually matches one to one. Benefits are multiple: - simpler code and API: the client will just have an IRQ, and nothing needs to be added in the adapter beside internally enabling it. - no more specific workqueue, the threading is handled by IRQ core directly (when required) - no more races when removing the device (the drivers are already required to disable irq on remove) - simpler handling for drivers: use plain regular IRQs - no more dependency on i2c-smbus for i2c-i801 (and any other adapter) - the IRQ domain is created automatically when the adapter exports the Host Notify capability - the IRQ are assign only if ACPI, OF and the caller did not assign one already - the domain is automatically destroyed on remove - fewer lines of code (minus 20, yeah!) Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--Documentation/i2c/smbus-protocol12
-rw-r--r--drivers/i2c/Kconfig1
-rw-r--r--drivers/i2c/busses/i2c-i801.c32
-rw-r--r--drivers/i2c/i2c-core.c113
-rw-r--r--drivers/i2c/i2c-smbus.c102
-rw-r--r--include/linux/i2c-smbus.h27
-rw-r--r--include/linux/i2c.h4
7 files changed, 133 insertions, 158 deletions
diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol
index 14d4ec1be245..092d474f5843 100644
--- a/Documentation/i2c/smbus-protocol
+++ b/Documentation/i2c/smbus-protocol
@@ -200,10 +200,14 @@ alerting device's address.
200[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P] 200[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]
201 201
202This is implemented in the following way in the Linux kernel: 202This is implemented in the following way in the Linux kernel:
203* I2C bus drivers which support SMBus Host Notify should call 203* I2C bus drivers which support SMBus Host Notify should report
204 i2c_setup_smbus_host_notify() to setup SMBus Host Notify support. 204 I2C_FUNC_SMBUS_HOST_NOTIFY.
205* I2C drivers for devices which can trigger SMBus Host Notify should implement 205* I2C bus drivers trigger SMBus Host Notify by a call to
206 the optional alert() callback. 206 i2c_handle_smbus_host_notify().
207* I2C drivers for devices which can trigger SMBus Host Notify will have
208 client->irq assigned to a Host Notify IRQ if noone else specified an other.
209
210There is currently no way to retrieve the data parameter from the client.
207 211
208 212
209Packet Error Checking (PEC) 213Packet Error Checking (PEC)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index d223650a97e4..de305f89a659 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -7,6 +7,7 @@ menu "I2C support"
7config I2C 7config I2C
8 tristate "I2C support" 8 tristate "I2C support"
9 select RT_MUTEXES 9 select RT_MUTEXES
10 select IRQ_DOMAIN
10 ---help--- 11 ---help---
11 I2C (pronounce: I-squared-C) is a slow serial bus protocol used in 12 I2C (pronounce: I-squared-C) is a slow serial bus protocol used in
12 many micro controller applications and developed by Philips. SMBus, 13 many micro controller applications and developed by Philips. SMBus,
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index c0c0cac9950c..e242db43774b 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -269,7 +269,6 @@ struct i801_priv {
269 */ 269 */
270 bool acpi_reserved; 270 bool acpi_reserved;
271 struct mutex acpi_lock; 271 struct mutex acpi_lock;
272 struct smbus_host_notify *host_notify;
273}; 272};
274 273
275#define FEATURE_SMBUS_PEC BIT(0) 274#define FEATURE_SMBUS_PEC BIT(0)
@@ -585,10 +584,10 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
585 584
586 /* 585 /*
587 * With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba) 586 * With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba)
588 * always returns 0 and is safe to read. 587 * always returns 0. Our current implementation doesn't provide
589 * We just use 0 given we have no use of the data right now. 588 * data, so we just ignore it.
590 */ 589 */
591 i2c_handle_smbus_host_notify(priv->host_notify, addr, 0); 590 i2c_handle_smbus_host_notify(&priv->adapter, addr);
592 591
593 /* clear Host Notify bit and return */ 592 /* clear Host Notify bit and return */
594 outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); 593 outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
@@ -951,17 +950,12 @@ static u32 i801_func(struct i2c_adapter *adapter)
951 I2C_FUNC_SMBUS_HOST_NOTIFY : 0); 950 I2C_FUNC_SMBUS_HOST_NOTIFY : 0);
952} 951}
953 952
954static int i801_enable_host_notify(struct i2c_adapter *adapter) 953static void i801_enable_host_notify(struct i2c_adapter *adapter)
955{ 954{
956 struct i801_priv *priv = i2c_get_adapdata(adapter); 955 struct i801_priv *priv = i2c_get_adapdata(adapter);
957 956
958 if (!(priv->features & FEATURE_HOST_NOTIFY)) 957 if (!(priv->features & FEATURE_HOST_NOTIFY))
959 return -ENOTSUPP; 958 return;
960
961 if (!priv->host_notify)
962 priv->host_notify = i2c_setup_smbus_host_notify(adapter);
963 if (!priv->host_notify)
964 return -ENOMEM;
965 959
966 priv->original_slvcmd = inb_p(SMBSLVCMD(priv)); 960 priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
967 961
@@ -971,8 +965,6 @@ static int i801_enable_host_notify(struct i2c_adapter *adapter)
971 965
972 /* clear Host Notify bit to allow a new notification */ 966 /* clear Host Notify bit to allow a new notification */
973 outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); 967 outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
974
975 return 0;
976} 968}
977 969
978static void i801_disable_host_notify(struct i801_priv *priv) 970static void i801_disable_host_notify(struct i801_priv *priv)
@@ -1647,14 +1639,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
1647 return err; 1639 return err;
1648 } 1640 }
1649 1641
1650 /* 1642 i801_enable_host_notify(&priv->adapter);
1651 * Enable Host Notify for chips that supports it.
1652 * It is done after i2c_add_adapter() so that we are sure the work queue
1653 * is not used if i2c_add_adapter() fails.
1654 */
1655 err = i801_enable_host_notify(&priv->adapter);
1656 if (err && err != -ENOTSUPP)
1657 dev_warn(&dev->dev, "Unable to enable SMBus Host Notify\n");
1658 1643
1659 i801_probe_optional_slaves(priv); 1644 i801_probe_optional_slaves(priv);
1660 /* We ignore errors - multiplexing is optional */ 1645 /* We ignore errors - multiplexing is optional */
@@ -1705,11 +1690,8 @@ static int i801_resume(struct device *dev)
1705{ 1690{
1706 struct pci_dev *pci_dev = to_pci_dev(dev); 1691 struct pci_dev *pci_dev = to_pci_dev(dev);
1707 struct i801_priv *priv = pci_get_drvdata(pci_dev); 1692 struct i801_priv *priv = pci_get_drvdata(pci_dev);
1708 int err;
1709 1693
1710 err = i801_enable_host_notify(&priv->adapter); 1694 i801_enable_host_notify(&priv->adapter);
1711 if (err && err != -ENOTSUPP)
1712 dev_warn(dev, "Unable to enable SMBus Host Notify\n");
1713 1695
1714 return 0; 1696 return 0;
1715} 1697}
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 8b93a262e237..3a1bc9c4efc7 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -65,6 +65,9 @@
65#define I2C_ADDR_OFFSET_TEN_BIT 0xa000 65#define I2C_ADDR_OFFSET_TEN_BIT 0xa000
66#define I2C_ADDR_OFFSET_SLAVE 0x1000 66#define I2C_ADDR_OFFSET_SLAVE 0x1000
67 67
68#define I2C_ADDR_7BITS_MAX 0x77
69#define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1)
70
68/* core_lock protects i2c_adapter_idr, and guarantees 71/* core_lock protects i2c_adapter_idr, and guarantees
69 that device detection, deletion of detected devices, and attach_adapter 72 that device detection, deletion of detected devices, and attach_adapter
70 calls are serialized */ 73 calls are serialized */
@@ -896,6 +899,25 @@ static void i2c_init_recovery(struct i2c_adapter *adap)
896 adap->bus_recovery_info = NULL; 899 adap->bus_recovery_info = NULL;
897} 900}
898 901
902static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
903{
904 struct i2c_adapter *adap = client->adapter;
905 unsigned int irq;
906
907 if (!adap->host_notify_domain)
908 return -ENXIO;
909
910 if (client->flags & I2C_CLIENT_TEN)
911 return -EINVAL;
912
913 irq = irq_find_mapping(adap->host_notify_domain, client->addr);
914 if (!irq)
915 irq = irq_create_mapping(adap->host_notify_domain,
916 client->addr);
917
918 return irq > 0 ? irq : -ENXIO;
919}
920
899static int i2c_device_probe(struct device *dev) 921static int i2c_device_probe(struct device *dev)
900{ 922{
901 struct i2c_client *client = i2c_verify_client(dev); 923 struct i2c_client *client = i2c_verify_client(dev);
@@ -917,6 +939,14 @@ static int i2c_device_probe(struct device *dev)
917 } 939 }
918 if (irq == -EPROBE_DEFER) 940 if (irq == -EPROBE_DEFER)
919 return irq; 941 return irq;
942 /*
943 * ACPI and OF did not find any useful IRQ, try to see
944 * if Host Notify can be used.
945 */
946 if (irq < 0) {
947 dev_dbg(dev, "Using Host Notify IRQ\n");
948 irq = i2c_smbus_host_notify_to_irq(client);
949 }
920 if (irq < 0) 950 if (irq < 0)
921 irq = 0; 951 irq = 0;
922 952
@@ -1866,6 +1896,79 @@ static const struct i2c_lock_operations i2c_adapter_lock_ops = {
1866 .unlock_bus = i2c_adapter_unlock_bus, 1896 .unlock_bus = i2c_adapter_unlock_bus,
1867}; 1897};
1868 1898
1899static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap)
1900{
1901 struct irq_domain *domain = adap->host_notify_domain;
1902 irq_hw_number_t hwirq;
1903
1904 if (!domain)
1905 return;
1906
1907 for (hwirq = 0 ; hwirq < I2C_ADDR_7BITS_COUNT ; hwirq++)
1908 irq_dispose_mapping(irq_find_mapping(domain, hwirq));
1909
1910 irq_domain_remove(domain);
1911 adap->host_notify_domain = NULL;
1912}
1913
1914static int i2c_host_notify_irq_map(struct irq_domain *h,
1915 unsigned int virq,
1916 irq_hw_number_t hw_irq_num)
1917{
1918 irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
1919
1920 return 0;
1921}
1922
1923static const struct irq_domain_ops i2c_host_notify_irq_ops = {
1924 .map = i2c_host_notify_irq_map,
1925};
1926
1927static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
1928{
1929 struct irq_domain *domain;
1930
1931 if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
1932 return 0;
1933
1934 domain = irq_domain_create_linear(adap->dev.fwnode,
1935 I2C_ADDR_7BITS_COUNT,
1936 &i2c_host_notify_irq_ops, adap);
1937 if (!domain)
1938 return -ENOMEM;
1939
1940 adap->host_notify_domain = domain;
1941
1942 return 0;
1943}
1944
1945/**
1946 * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
1947 * I2C client.
1948 * @adap: the adapter
1949 * @addr: the I2C address of the notifying device
1950 * Context: can't sleep
1951 *
1952 * Helper function to be called from an I2C bus driver's interrupt
1953 * handler. It will schedule the Host Notify IRQ.
1954 */
1955int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
1956{
1957 int irq;
1958
1959 if (!adap)
1960 return -EINVAL;
1961
1962 irq = irq_find_mapping(adap->host_notify_domain, addr);
1963 if (irq <= 0)
1964 return -ENXIO;
1965
1966 generic_handle_irq(irq);
1967
1968 return 0;
1969}
1970EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
1971
1869static int i2c_register_adapter(struct i2c_adapter *adap) 1972static int i2c_register_adapter(struct i2c_adapter *adap)
1870{ 1973{
1871 int res = -EINVAL; 1974 int res = -EINVAL;
@@ -1897,6 +2000,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
1897 if (adap->timeout == 0) 2000 if (adap->timeout == 0)
1898 adap->timeout = HZ; 2001 adap->timeout = HZ;
1899 2002
2003 /* register soft irqs for Host Notify */
2004 res = i2c_setup_host_notify_irq_domain(adap);
2005 if (res) {
2006 pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
2007 adap->name, res);
2008 goto out_list;
2009 }
2010
1900 dev_set_name(&adap->dev, "i2c-%d", adap->nr); 2011 dev_set_name(&adap->dev, "i2c-%d", adap->nr);
1901 adap->dev.bus = &i2c_bus_type; 2012 adap->dev.bus = &i2c_bus_type;
1902 adap->dev.type = &i2c_adapter_type; 2013 adap->dev.type = &i2c_adapter_type;
@@ -2134,6 +2245,8 @@ void i2c_del_adapter(struct i2c_adapter *adap)
2134 2245
2135 pm_runtime_disable(&adap->dev); 2246 pm_runtime_disable(&adap->dev);
2136 2247
2248 i2c_host_notify_irq_teardown(adap);
2249
2137 /* wait until all references to the device are gone 2250 /* wait until all references to the device are gone
2138 * 2251 *
2139 * FIXME: This is old code and should ideally be replaced by an 2252 * FIXME: This is old code and should ideally be replaced by an
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index b0d2679c60d1..f9271c713d20 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -241,108 +241,6 @@ int i2c_handle_smbus_alert(struct i2c_client *ara)
241} 241}
242EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); 242EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
243 243
244static void smbus_host_notify_work(struct work_struct *work)
245{
246 struct alert_data alert;
247 struct i2c_adapter *adapter;
248 unsigned long flags;
249 u16 payload;
250 u8 addr;
251 struct smbus_host_notify *data;
252
253 data = container_of(work, struct smbus_host_notify, work);
254
255 spin_lock_irqsave(&data->lock, flags);
256 payload = data->payload;
257 addr = data->addr;
258 adapter = data->adapter;
259
260 /* clear the pending bit and release the spinlock */
261 data->pending = false;
262 spin_unlock_irqrestore(&data->lock, flags);
263
264 if (!adapter || !addr)
265 return;
266
267 alert.type = I2C_PROTOCOL_SMBUS_HOST_NOTIFY;
268 alert.addr = addr;
269 alert.data = payload;
270
271 device_for_each_child(&adapter->dev, &alert, smbus_do_alert);
272}
273
274/**
275 * i2c_setup_smbus_host_notify - Allocate a new smbus_host_notify for the given
276 * I2C adapter.
277 * @adapter: the adapter we want to associate a Host Notify function
278 *
279 * Returns a struct smbus_host_notify pointer on success, and NULL on failure.
280 * The resulting smbus_host_notify must not be freed afterwards, it is a
281 * managed resource already.
282 */
283struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
284{
285 struct smbus_host_notify *host_notify;
286
287 host_notify = devm_kzalloc(&adap->dev, sizeof(struct smbus_host_notify),
288 GFP_KERNEL);
289 if (!host_notify)
290 return NULL;
291
292 host_notify->adapter = adap;
293
294 spin_lock_init(&host_notify->lock);
295 INIT_WORK(&host_notify->work, smbus_host_notify_work);
296
297 return host_notify;
298}
299EXPORT_SYMBOL_GPL(i2c_setup_smbus_host_notify);
300
301/**
302 * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
303 * I2C client.
304 * @host_notify: the struct host_notify attached to the relevant adapter
305 * @addr: the I2C address of the notifying device
306 * @data: the payload of the notification
307 * Context: can't sleep
308 *
309 * Helper function to be called from an I2C bus driver's interrupt
310 * handler. It will schedule the Host Notify work, in turn calling the
311 * corresponding I2C device driver's alert function.
312 *
313 * host_notify should be a valid pointer previously returned by
314 * i2c_setup_smbus_host_notify().
315 */
316int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
317 unsigned short addr, unsigned int data)
318{
319 unsigned long flags;
320 struct i2c_adapter *adapter;
321
322 if (!host_notify || !host_notify->adapter)
323 return -EINVAL;
324
325 adapter = host_notify->adapter;
326
327 spin_lock_irqsave(&host_notify->lock, flags);
328
329 if (host_notify->pending) {
330 spin_unlock_irqrestore(&host_notify->lock, flags);
331 dev_warn(&adapter->dev, "Host Notify already scheduled.\n");
332 return -EBUSY;
333 }
334
335 host_notify->payload = data;
336 host_notify->addr = addr;
337
338 /* Mark that there is a pending notification and release the lock */
339 host_notify->pending = true;
340 spin_unlock_irqrestore(&host_notify->lock, flags);
341
342 return schedule_work(&host_notify->work);
343}
344EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
345
346module_i2c_driver(smbalert_driver); 244module_i2c_driver(smbalert_driver);
347 245
348MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 246MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
index c2e3324f9468..a1385023a29b 100644
--- a/include/linux/i2c-smbus.h
+++ b/include/linux/i2c-smbus.h
@@ -50,31 +50,4 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
50 struct i2c_smbus_alert_setup *setup); 50 struct i2c_smbus_alert_setup *setup);
51int i2c_handle_smbus_alert(struct i2c_client *ara); 51int i2c_handle_smbus_alert(struct i2c_client *ara);
52 52
53/**
54 * smbus_host_notify - internal structure used by the Host Notify mechanism.
55 * @adapter: the I2C adapter associated with this struct
56 * @work: worker used to schedule the IRQ in the slave device
57 * @lock: spinlock to check if a notification is already pending
58 * @pending: flag set when a notification is pending (any new notification will
59 * be rejected if pending is true)
60 * @payload: the actual payload of the Host Notify event
61 * @addr: the address of the slave device which raised the notification
62 *
63 * This struct needs to be allocated by i2c_setup_smbus_host_notify() and does
64 * not need to be freed. Internally, i2c_setup_smbus_host_notify() uses a
65 * managed resource to clean this up when the adapter get released.
66 */
67struct smbus_host_notify {
68 struct i2c_adapter *adapter;
69 struct work_struct work;
70 spinlock_t lock;
71 bool pending;
72 u16 payload;
73 u8 addr;
74};
75
76struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap);
77int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
78 unsigned short addr, unsigned int data);
79
80#endif /* _LINUX_I2C_SMBUS_H */ 53#endif /* _LINUX_I2C_SMBUS_H */
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 82cf90945bb8..b2109c522dec 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -30,6 +30,7 @@
30#include <linux/device.h> /* for struct device */ 30#include <linux/device.h> /* for struct device */
31#include <linux/sched.h> /* for completion */ 31#include <linux/sched.h> /* for completion */
32#include <linux/mutex.h> 32#include <linux/mutex.h>
33#include <linux/irqdomain.h> /* for Host Notify IRQ */
33#include <linux/of.h> /* for struct device_node */ 34#include <linux/of.h> /* for struct device_node */
34#include <linux/swab.h> /* for swab16 */ 35#include <linux/swab.h> /* for swab16 */
35#include <uapi/linux/i2c.h> 36#include <uapi/linux/i2c.h>
@@ -575,6 +576,8 @@ struct i2c_adapter {
575 576
576 struct i2c_bus_recovery_info *bus_recovery_info; 577 struct i2c_bus_recovery_info *bus_recovery_info;
577 const struct i2c_adapter_quirks *quirks; 578 const struct i2c_adapter_quirks *quirks;
579
580 struct irq_domain *host_notify_domain;
578}; 581};
579#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) 582#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
580 583
@@ -747,6 +750,7 @@ static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
747 return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); 750 return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
748} 751}
749 752
753int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
750/** 754/**
751 * module_i2c_driver() - Helper macro for registering a modular I2C driver 755 * module_i2c_driver() - Helper macro for registering a modular I2C driver
752 * @__i2c_driver: i2c_driver struct 756 * @__i2c_driver: i2c_driver struct