Linux I2C development
 help / color / mirror / Atom feed
* [PATCH v4 6/8] i2c: use an IRQ to report Host Notify events, not alert
From: Benjamin Tissoires @ 2016-10-10 16:42 UTC (permalink / raw)
  To: Wolfram Sang, Dmitry Torokhov
  Cc: Jean Delvare, Jonathan Corbet, KT Liao, linux-i2c, linux-input,
	linux-doc, linux-kernel
In-Reply-To: <1476117755-8113-1-git-send-email-benjamin.tissoires@redhat.com>

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 just needs to get the IRQ through
  i2c_smbus_host_notify_to_irq(), 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, but the IRQ are allocated on demand
- the domain is automatically destroyed on remove
- fewer lines of code (minus 20, yeah!)

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v4
---
 Documentation/i2c/smbus-protocol |  10 ++--
 drivers/i2c/busses/i2c-i801.c    |  31 +++--------
 drivers/i2c/i2c-core.c           | 117 +++++++++++++++++++++++++++++++++++++++
 drivers/i2c/i2c-smbus.c          | 102 ----------------------------------
 include/linux/i2c-smbus.h        |  27 ---------
 include/linux/i2c.h              |   5 ++
 6 files changed, 135 insertions(+), 157 deletions(-)

diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol
index 14d4ec1..8a29ba3 100644
--- a/Documentation/i2c/smbus-protocol
+++ b/Documentation/i2c/smbus-protocol
@@ -200,10 +200,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.
+* I2C bus drivers which support SMBus Host Notify should report
+  I2C_FUNC_SMBUS_HOST_NOTIFY.
+* I2C drivers for devices which can trigger SMBus Host Notify should request
+  a Host Notify IRQ through i2c_smbus_host_notify_to_irq().
+
+There is currently no way to retrieve the data parameter from the client.
 
 
 Packet Error Checking (PEC)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 42a6a89..a016b48 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -266,7 +266,6 @@ struct i801_priv {
 	 */
 	bool acpi_reserved;
 	struct mutex acpi_lock;
-	struct smbus_host_notify *host_notify;
 };
 
 #define FEATURE_SMBUS_PEC	BIT(0)
@@ -582,10 +581,10 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
 
 	/*
 	 * With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba)
-	 * always returns 0 and is safe to read.
-	 * We just use 0 given we have no use of the data right now.
+	 * always returns 0. Our current implementation doesn't provide
+	 * data, so we just ignore it.
 	 */
-	i2c_handle_smbus_host_notify(priv->host_notify, addr, 0);
+	i2c_handle_smbus_host_notify(&priv->adapter, addr);
 
 	/* clear Host Notify bit and return */
 	outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
@@ -941,17 +940,12 @@ static u32 i801_func(struct i2c_adapter *adapter)
 		I2C_FUNC_SMBUS_HOST_NOTIFY : 0);
 }
 
-static int i801_enable_host_notify(struct i2c_adapter *adapter)
+static void i801_enable_host_notify(struct i2c_adapter *adapter)
 {
 	struct i801_priv *priv = i2c_get_adapdata(adapter);
 
 	if (!(priv->features & FEATURE_HOST_NOTIFY))
-		return -ENOTSUPP;
-
-	if (!priv->host_notify)
-		priv->host_notify = i2c_setup_smbus_host_notify(adapter);
-	if (!priv->host_notify)
-		return -ENOMEM;
+		return;
 
 	priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
 
@@ -961,8 +955,6 @@ static int i801_enable_host_notify(struct i2c_adapter *adapter)
 
 	/* clear Host Notify bit to allow a new notification */
 	outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
-
-	return 0;
 }
 
 static void i801_disable_host_notify(struct i801_priv *priv)
@@ -1631,14 +1623,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
 		return err;
 	}
 
-	/*
-	 * Enable Host Notify for chips that supports it.
-	 * It is done after i2c_add_adapter() so that we are sure the work queue
-	 * is not used if i2c_add_adapter() fails.
-	 */
-	err = i801_enable_host_notify(&priv->adapter);
-	if (err && err != -ENOTSUPP)
-		dev_warn(&dev->dev, "Unable to enable SMBus Host Notify\n");
+	i801_enable_host_notify(&priv->adapter);
 
 	i801_probe_optional_slaves(priv);
 	/* We ignore errors - multiplexing is optional */
@@ -1691,9 +1676,7 @@ static int i801_resume(struct device *dev)
 	struct i801_priv *priv = pci_get_drvdata(pci_dev);
 	int err;
 
-	err = i801_enable_host_notify(&priv->adapter);
-	if (err && err != -ENOTSUPP)
-		dev_warn(dev, "Unable to enable SMBus Host Notify\n");
+	i801_enable_host_notify(&priv->adapter);
 
 	return 0;
 }
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 79ce9e1..9d5bcf9 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -65,6 +65,9 @@
 #define I2C_ADDR_OFFSET_TEN_BIT	0xa000
 #define I2C_ADDR_OFFSET_SLAVE	0x1000
 
+#define I2C_ADDR_7BITS_MAX	0x77
+#define I2C_ADDR_7BITS_COUNT	(I2C_ADDR_7BITS_MAX + 1)
+
 /* core_lock protects i2c_adapter_idr, and guarantees
    that device detection, deletion of detected devices, and attach_adapter
    calls are serialized */
@@ -1781,6 +1784,110 @@ static const struct i2c_lock_operations i2c_adapter_lock_ops = {
 	.unlock_bus =  i2c_adapter_unlock_bus,
 };
 
+static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap)
+{
+	struct irq_domain *domain = adap->host_notify_domain;
+	irq_hw_number_t hwirq;
+
+	if (!domain)
+		return;
+
+	for (hwirq = 0 ; hwirq < I2C_ADDR_7BITS_COUNT ; hwirq++)
+		irq_dispose_mapping(irq_find_mapping(domain, hwirq));
+
+	irq_domain_remove(domain);
+	adap->host_notify_domain = NULL;
+}
+
+static int i2c_host_notify_irq_map(struct irq_domain *h,
+					  unsigned int virq,
+					  irq_hw_number_t hw_irq_num)
+{
+	irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
+
+	return 0;
+}
+
+static struct irq_domain_ops i2c_host_notify_irq_ops = {
+	.map = i2c_host_notify_irq_map,
+};
+
+static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
+{
+	struct irq_domain *domain;
+
+	if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
+		return 0;
+
+	domain = irq_domain_create_linear(adap->dev.fwnode,
+					  I2C_ADDR_7BITS_COUNT,
+					  &i2c_host_notify_irq_ops, adap);
+	if (!domain)
+		return -ENOMEM;
+
+	adap->host_notify_domain = domain;
+
+	return 0;
+}
+
+/**
+ * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
+ * I2C client.
+ * @adap: the adapter
+ * @addr: the I2C address of the notifying device
+ * Context: can't sleep
+ *
+ * Helper function to be called from an I2C bus driver's interrupt
+ * handler. It will schedule the Host Notify IRQ.
+ */
+int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
+{
+	int irq;
+
+	if (!adap)
+		return -EINVAL;
+
+	irq = irq_find_mapping(adap->host_notify_domain, addr);
+	if (irq <= 0)
+		return -ENXIO;
+
+	generic_handle_irq(irq);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
+
+/**
+ * i2c_smbus_host_notify_to_irq - Gets a Host Notify IRQ number associated to
+ * the I2C client.
+ * @client: the I2C client
+ *
+ * Will return a valid IRQ number or an error. The Host Notify interrupts
+ * only work currently with 7 bits addresses.
+ */
+int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
+{
+	struct i2c_adapter *adap = client->adapter;
+	unsigned int irq;
+
+	if (!adap->host_notify_domain)
+		return -ENXIO;
+
+	if (client->flags & I2C_CLIENT_TEN) {
+		dev_err(&client->dev,
+			"Host Notify not supported on 10 bits addresses.\n");
+		return -EINVAL;
+	}
+
+	irq = irq_find_mapping(adap->host_notify_domain, client->addr);
+	if (!irq)
+		irq = irq_create_mapping(adap->host_notify_domain,
+					 client->addr);
+
+	return irq > 0 ? irq : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(i2c_smbus_host_notify_to_irq);
+
 static int i2c_register_adapter(struct i2c_adapter *adap)
 {
 	int res = -EINVAL;
@@ -1812,6 +1919,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
 	if (adap->timeout == 0)
 		adap->timeout = HZ;
 
+	/* register soft irqs for Host Notify */
+	res = i2c_setup_host_notify_irq_domain(adap);
+	if (res) {
+		pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
+		       adap->name, res);
+		goto out_list;
+	}
+
 	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
 	adap->dev.bus = &i2c_bus_type;
 	adap->dev.type = &i2c_adapter_type;
@@ -2049,6 +2164,8 @@ void i2c_del_adapter(struct i2c_adapter *adap)
 
 	pm_runtime_disable(&adap->dev);
 
+	i2c_host_notify_irq_teardown(adap);
+
 	/* wait until all references to the device are gone
 	 *
 	 * 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 b0d2679..f9271c7 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)
 }
 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
- * @addr: the I2C address of the notifying device
- * @data: the payload of the notification
- * 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 <jdelvare@suse.de>");
diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
index c2e3324..a138502 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,
 					 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;
-};
-
-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);
-
 #endif /* _LINUX_I2C_SMBUS_H */
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 4a4099d..5fbef64 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -30,6 +30,7 @@
 #include <linux/device.h>	/* for struct device */
 #include <linux/sched.h>	/* for completion */
 #include <linux/mutex.h>
+#include <linux/irqdomain.h>		/* for Host Notify IRQ */
 #include <linux/of.h>		/* for struct device_node */
 #include <linux/swab.h>		/* for swab16 */
 #include <uapi/linux/i2c.h>
@@ -567,6 +568,8 @@ struct i2c_adapter {
 
 	struct i2c_bus_recovery_info *bus_recovery_info;
 	const struct i2c_adapter_quirks *quirks;
+
+	struct irq_domain *host_notify_domain;
 };
 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
 
@@ -738,6 +741,8 @@ static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
 	return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
 }
 
+int i2c_smbus_host_notify_to_irq(const struct i2c_client *client);
+int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
 /**
  * module_i2c_driver() - Helper macro for registering a modular I2C driver
  * @__i2c_driver: i2c_driver struct
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 5/8] i2c: i801: remove SMBNTFDDAT reads as they always seem to return 0
From: Benjamin Tissoires @ 2016-10-10 16:42 UTC (permalink / raw)
  To: Wolfram Sang, Dmitry Torokhov
  Cc: Jean Delvare, Jonathan Corbet, KT Liao, linux-i2c, linux-input,
	linux-doc, linux-kernel
In-Reply-To: <1476117755-8113-1-git-send-email-benjamin.tissoires@redhat.com>

On the platform tested, reading SMBNTFDDAT always returns 0 (using 1 read
of a word or 2 of 2 bytes). Given that we are not sure why and that we
don't need to rely on the data parameter in the current users of Host
Notify, remove this part of the code.

If someone wants to re-enable it, just revert this commit and data should
be available.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

no changes in v4

no changes in v3

new in v2
---
 drivers/i2c/busses/i2c-i801.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 88b2e31..42a6a89 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -117,7 +117,6 @@
 #define SMBSLVSTS(p)	(16 + (p)->smba)	/* ICH3 and later */
 #define SMBSLVCMD(p)	(17 + (p)->smba)	/* ICH3 and later */
 #define SMBNTFDADD(p)	(20 + (p)->smba)	/* ICH3 and later */
-#define SMBNTFDDAT(p)	(22 + (p)->smba)	/* ICH3 and later */
 
 /* PCI Address Constants */
 #define SMBBAR		4
@@ -578,12 +577,15 @@ static void i801_isr_byte_done(struct i801_priv *priv)
 static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
 {
 	unsigned short addr;
-	unsigned int data;
 
 	addr = inb_p(SMBNTFDADD(priv)) >> 1;
-	data = inw_p(SMBNTFDDAT(priv));
 
-	i2c_handle_smbus_host_notify(priv->host_notify, addr, data);
+	/*
+	 * With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba)
+	 * always returns 0 and is safe to read.
+	 * We just use 0 given we have no use of the data right now.
+	 */
+	i2c_handle_smbus_host_notify(priv->host_notify, addr, 0);
 
 	/* clear Host Notify bit and return */
 	outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 4/8] i2c: i801: use the BIT() macro for FEATURES_* also
From: Benjamin Tissoires @ 2016-10-10 16:42 UTC (permalink / raw)
  To: Wolfram Sang, Dmitry Torokhov
  Cc: Jean Delvare, Jonathan Corbet, KT Liao, linux-i2c, linux-input,
	linux-doc, linux-kernel
In-Reply-To: <1476117755-8113-1-git-send-email-benjamin.tissoires@redhat.com>

no functional changes

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

no changes in v4

no changes in v3

new in v2
---
 drivers/i2c/busses/i2c-i801.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 5928ee2..88b2e31 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -270,15 +270,15 @@ struct i801_priv {
 	struct smbus_host_notify *host_notify;
 };
 
-#define FEATURE_SMBUS_PEC	(1 << 0)
-#define FEATURE_BLOCK_BUFFER	(1 << 1)
-#define FEATURE_BLOCK_PROC	(1 << 2)
-#define FEATURE_I2C_BLOCK_READ	(1 << 3)
-#define FEATURE_IRQ		(1 << 4)
-#define FEATURE_HOST_NOTIFY	(1 << 5)
+#define FEATURE_SMBUS_PEC	BIT(0)
+#define FEATURE_BLOCK_BUFFER	BIT(1)
+#define FEATURE_BLOCK_PROC	BIT(2)
+#define FEATURE_I2C_BLOCK_READ	BIT(3)
+#define FEATURE_IRQ		BIT(4)
+#define FEATURE_HOST_NOTIFY	BIT(5)
 /* Not really a feature, but it's convenient to handle it as such */
-#define FEATURE_IDF		(1 << 15)
-#define FEATURE_TCO		(1 << 16)
+#define FEATURE_IDF		BIT(15)
+#define FEATURE_TCO		BIT(16)
 
 static const char *i801_feature_names[] = {
 	"SMBus PEC",
-- 
2.7.4


^ permalink raw reply related

* [PATCH v4 3/8] i2c: i801: use BIT() macro for bits definition
From: Benjamin Tissoires @ 2016-10-10 16:42 UTC (permalink / raw)
  To: Wolfram Sang, Dmitry Torokhov
  Cc: Jean Delvare, Jonathan Corbet, KT Liao, linux-i2c, linux-input,
	linux-doc, linux-kernel
In-Reply-To: <1476117755-8113-1-git-send-email-benjamin.tissoires@redhat.com>

i801 mixes hexadecimal and decimal values for defining bits. However,
we have a nice BIT() macro for this exact purpose.

No functional changes, cleanup only.

Reviewed-by: Jean Delvare <jdelvare@suse.de>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

no changes in v4

no changes in v3

no changes in v2
---
 drivers/i2c/busses/i2c-i801.c | 50 +++++++++++++++++++++----------------------
 1 file changed, 25 insertions(+), 25 deletions(-)

diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index cfb74fc..5928ee2 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -136,26 +136,26 @@
 #define SBREG_SMBCTRL		0xc6000c
 
 /* Host status bits for SMBPCISTS */
-#define SMBPCISTS_INTS		0x08
+#define SMBPCISTS_INTS		BIT(3)
 
 /* Control bits for SMBPCICTL */
-#define SMBPCICTL_INTDIS	0x0400
+#define SMBPCICTL_INTDIS	BIT(10)
 
 /* Host configuration bits for SMBHSTCFG */
-#define SMBHSTCFG_HST_EN	1
-#define SMBHSTCFG_SMB_SMI_EN	2
-#define SMBHSTCFG_I2C_EN	4
+#define SMBHSTCFG_HST_EN	BIT(0)
+#define SMBHSTCFG_SMB_SMI_EN	BIT(1)
+#define SMBHSTCFG_I2C_EN	BIT(2)
 
 /* TCO configuration bits for TCOCTL */
-#define TCOCTL_EN		0x0100
+#define TCOCTL_EN		BIT(8)
 
 /* Auxiliary status register bits, ICH4+ only */
-#define SMBAUXSTS_CRCE		1
-#define SMBAUXSTS_STCO		2
+#define SMBAUXSTS_CRCE		BIT(0)
+#define SMBAUXSTS_STCO		BIT(1)
 
 /* Auxiliary control register bits, ICH4+ only */
-#define SMBAUXCTL_CRC		1
-#define SMBAUXCTL_E32B		2
+#define SMBAUXCTL_CRC		BIT(0)
+#define SMBAUXCTL_E32B		BIT(1)
 
 /* Other settings */
 #define MAX_RETRIES		400
@@ -170,27 +170,27 @@
 #define I801_I2C_BLOCK_DATA	0x18	/* ICH5 and later */
 
 /* I801 Host Control register bits */
-#define SMBHSTCNT_INTREN	0x01
-#define SMBHSTCNT_KILL		0x02
-#define SMBHSTCNT_LAST_BYTE	0x20
-#define SMBHSTCNT_START		0x40
-#define SMBHSTCNT_PEC_EN	0x80	/* ICH3 and later */
+#define SMBHSTCNT_INTREN	BIT(0)
+#define SMBHSTCNT_KILL		BIT(1)
+#define SMBHSTCNT_LAST_BYTE	BIT(5)
+#define SMBHSTCNT_START		BIT(6)
+#define SMBHSTCNT_PEC_EN	BIT(7)	/* ICH3 and later */
 
 /* I801 Hosts Status register bits */
-#define SMBHSTSTS_BYTE_DONE	0x80
-#define SMBHSTSTS_INUSE_STS	0x40
-#define SMBHSTSTS_SMBALERT_STS	0x20
-#define SMBHSTSTS_FAILED	0x10
-#define SMBHSTSTS_BUS_ERR	0x08
-#define SMBHSTSTS_DEV_ERR	0x04
-#define SMBHSTSTS_INTR		0x02
-#define SMBHSTSTS_HOST_BUSY	0x01
+#define SMBHSTSTS_BYTE_DONE	BIT(7)
+#define SMBHSTSTS_INUSE_STS	BIT(6)
+#define SMBHSTSTS_SMBALERT_STS	BIT(5)
+#define SMBHSTSTS_FAILED	BIT(4)
+#define SMBHSTSTS_BUS_ERR	BIT(3)
+#define SMBHSTSTS_DEV_ERR	BIT(2)
+#define SMBHSTSTS_INTR		BIT(1)
+#define SMBHSTSTS_HOST_BUSY	BIT(0)
 
 /* Host Notify Status register bits */
-#define SMBSLVSTS_HST_NTFY_STS	1
+#define SMBSLVSTS_HST_NTFY_STS	BIT(0)
 
 /* Host Notify Command register bits */
-#define SMBSLVCMD_HST_NTFY_INTREN	0x01
+#define SMBSLVCMD_HST_NTFY_INTREN	BIT(0)
 
 #define STATUS_ERROR_FLAGS	(SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \
 				 SMBHSTSTS_DEV_ERR)
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 2/8] i2c: i801: minor formatting issues
From: Benjamin Tissoires @ 2016-10-10 16:42 UTC (permalink / raw)
  To: Wolfram Sang, Dmitry Torokhov
  Cc: Jean Delvare, Jonathan Corbet, KT Liao, linux-i2c, linux-input,
	linux-doc, linux-kernel
In-Reply-To: <1476117755-8113-1-git-send-email-benjamin.tissoires@redhat.com>

No functional changes, just typos and remove unused #define.

Reviewed-by: Jean Delvare <jdelvare@suse.de>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

no changes in v4

no changes in v3

no changes in v2
---
 drivers/i2c/busses/i2c-i801.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 3a2fdf5..cfb74fc 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -186,10 +186,10 @@
 #define SMBHSTSTS_INTR		0x02
 #define SMBHSTSTS_HOST_BUSY	0x01
 
-/* Host Notify Status registers bits */
+/* Host Notify Status register bits */
 #define SMBSLVSTS_HST_NTFY_STS	1
 
-/* Host Notify Command registers bits */
+/* Host Notify Command register bits */
 #define SMBSLVCMD_HST_NTFY_INTREN	0x01
 
 #define STATUS_ERROR_FLAGS	(SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \
@@ -270,8 +270,6 @@ struct i801_priv {
 	struct smbus_host_notify *host_notify;
 };
 
-#define SMBHSTNTFY_SIZE		8
-
 #define FEATURE_SMBUS_PEC	(1 << 0)
 #define FEATURE_BLOCK_BUFFER	(1 << 1)
 #define FEATURE_BLOCK_PROC	(1 << 2)
-- 
2.7.4


^ permalink raw reply related

* [PATCH v4 1/8] i2c: i801: store and restore the SLVCMD register at load and unload
From: Benjamin Tissoires @ 2016-10-10 16:42 UTC (permalink / raw)
  To: Wolfram Sang, Dmitry Torokhov
  Cc: Jean Delvare, Jonathan Corbet, KT Liao, linux-i2c, linux-input,
	linux-doc, linux-kernel
In-Reply-To: <1476117755-8113-1-git-send-email-benjamin.tissoires@redhat.com>

Also do not override any other configuration in this register.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v4:
- add the i801_disable_host_notify function here as this gets the
  first in the series

no changes in v3

new in v2
---
 drivers/i2c/busses/i2c-i801.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 22a0ed4..3a2fdf5 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -240,6 +240,7 @@ struct i801_priv {
 	struct i2c_adapter adapter;
 	unsigned long smba;
 	unsigned char original_hstcfg;
+	unsigned char original_slvcmd;
 	struct pci_dev *pci_dev;
 	unsigned int features;
 
@@ -952,13 +953,26 @@ static int i801_enable_host_notify(struct i2c_adapter *adapter)
 	if (!priv->host_notify)
 		return -ENOMEM;
 
-	outb_p(SMBSLVCMD_HST_NTFY_INTREN, SMBSLVCMD(priv));
+	priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
+
+	if (!(SMBSLVCMD_HST_NTFY_INTREN & priv->original_slvcmd))
+		outb_p(SMBSLVCMD_HST_NTFY_INTREN | priv->original_slvcmd,
+		       SMBSLVCMD(priv));
+
 	/* clear Host Notify bit to allow a new notification */
 	outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
 
 	return 0;
 }
 
+static void i801_disable_host_notify(struct i801_priv *priv)
+{
+	if (!(priv->features & FEATURE_HOST_NOTIFY))
+		return;
+
+	outb_p(priv->original_slvcmd, SMBSLVCMD(priv));
+}
+
 static const struct i2c_algorithm smbus_algorithm = {
 	.smbus_xfer	= i801_access,
 	.functionality	= i801_func,
@@ -1647,6 +1661,7 @@ static void i801_remove(struct pci_dev *dev)
 	pm_runtime_forbid(&dev->dev);
 	pm_runtime_get_noresume(&dev->dev);
 
+	i801_disable_host_notify(priv);
 	i801_del_mux(priv);
 	i2c_del_adapter(&priv->adapter);
 	i801_acpi_remove(priv);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 0/8] i2c: Host Notify / i801 fixes
From: Benjamin Tissoires @ 2016-10-10 16:42 UTC (permalink / raw)
  To: Wolfram Sang, Dmitry Torokhov
  Cc: Jean Delvare, Jonathan Corbet, KT Liao, linux-i2c, linux-input,
	linux-doc, linux-kernel

Hi Wolfram, Dmitry,

this is a respin of the series "i2c: Host Notify / i801 fixes".
The changes have been driven by Dmitry who made me realise that using
.alert() was not good, while using an irqchip was a much better choice.

I have dropped in the series the fixes for i2c-smbus given that the code
gets removed. This new code is IMO simpler and provide a better interface
for everybody (adapter and clients), see patch 8/8.

Dmitry, the changes in the Elan driver are simple enough, and I wonder if
they could not go through Wolfram's tree. I have other pending patches for
elan_i2c (trackstick and binding from PS/2) so maybe this might not be the
best solution to have the I2C tree taking the changes.
Also, if this gets merged, that would mean for RMI4, only the HID backend
will not be using IRQ, but we could do the same IRQ reporting than here.

Cheers,
Benjamin

Benjamin Tissoires (8):
  i2c: i801: store and restore the SLVCMD register at load and unload
  i2c: i801: minor formatting issues
  i2c: i801: use BIT() macro for bits definition
  i2c: i801: use the BIT() macro for FEATURES_* also
  i2c: i801: remove SMBNTFDDAT reads as they always seem to return 0
  i2c: use an IRQ to report Host Notify events, not alert
  Input: elan_i2c - store the irq in struct elan_tp_data
  Input: elan_i2c - add Host Notify support

 Documentation/i2c/smbus-protocol    |  10 +--
 drivers/i2c/busses/i2c-i801.c       | 120 ++++++++++++++++++------------------
 drivers/i2c/i2c-core.c              | 117 +++++++++++++++++++++++++++++++++++
 drivers/i2c/i2c-smbus.c             | 102 ------------------------------
 drivers/input/mouse/elan_i2c_core.c |  49 +++++++++++----
 include/linux/i2c-smbus.h           |  27 --------
 include/linux/i2c.h                 |   5 ++
 7 files changed, 224 insertions(+), 206 deletions(-)

-- 
2.7.4


^ permalink raw reply

* Re: [PATCH 04/10] i2c: i2c-mux-i2cs: Add device tree bindings
From: Peter Rosin @ 2016-10-10 15:48 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
	Jean Delvare, Avirup Banerjee, Georgi Vlaev, Guenter Roeck,
	JawaharBalaji Thirumalaisamy, devicetree, linux-kernel,
	linux-gpio, linux-i2c, linux-leds, linux-hwmon
In-Reply-To: <1475853669-22480-5-git-send-email-pantelis.antoniou@konsulko.com>

On 2016-10-07 17:21, Pantelis Antoniou wrote:
> From: Georgi Vlaev <gvlaev@juniper.net>
> 
> Add binding document for the i2c mux driver of Juniper's I2CS FPGA.
> 
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
>  .../devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt   | 27 ++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
> 
> diff --git a/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
> new file mode 100644
> index 0000000..03d917f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
> @@ -0,0 +1,27 @@
> +* Juniper I2C Mux on I2CS
> +
> +	I2C mux driver for switching the RE access to the FPC i2c bus.
> +	Compatible with the FPC variant of the I2CS.
> +
> +Required properties:
> +
> +  - compatible: "jnx,i2cs-mux-i2cs".

jnx,i2c-mux-i2cs

> +
> +The following required properties are defined externally:
> +
> +  - Standard I2C mux properties. See i2c-mux.txt in this directory.

To accommodate changes pending for 4.9, change "mux" to "gate" in
this line...

> +  - I2C child bus nodes. See i2c-mux.txt in this directory.

This line is wrong since the child nodes themselves are optional in
i2c-gate.txt (and i2c-mux.txt). I guess you can just drop it since
the child nodes are mentioned in i2c-gate.txt (and i2c-mux.txt).

> +
> +Example:
> +
> +fpc0_mux {
> +	compatible = "jnx,i2c-mux-i2cs";
> +	#address-cells = <1>;
> +	#size-cells = <0>;

...drop these two...

> +
> +	fpc0i2c0: i2c@0 {

...change i2c@0 to i2c-gate...

> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		reg = <0>;

...and drop reg.

Cheers,
Peter

> +	};
> +};
> 


^ permalink raw reply

* Re: [PATCH 03/10] i2c/muxes: Juniper I2CS RE mux
From: Peter Rosin @ 2016-10-10 15:29 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
	Jean Delvare, Avirup Banerjee, Georgi Vlaev, Guenter Roeck,
	JawaharBalaji Thirumalaisamy, devicetree, linux-kernel,
	linux-gpio, linux-i2c, linux-leds, linux-hwmon
In-Reply-To: <1475853669-22480-4-git-send-email-pantelis.antoniou@konsulko.com>

On 2016-10-07 17:21, Pantelis Antoniou wrote:
> From: Georgi Vlaev <gvlaev@juniper.net>
> 
> Add support for Juniper I2C Slave RE multiplexer driver.
> 
> This I2C multiplexer driver allows the RE to access some of
> the FPC I2C buses. It's compatible only with the FPC variant of the
> I2CS.
> 
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> Signed-off-by: Guenter Roeck <groeck@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
>  drivers/i2c/muxes/Kconfig        |  10 +++
>  drivers/i2c/muxes/Makefile       |   1 +
>  drivers/i2c/muxes/i2c-mux-i2cs.c | 155 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 166 insertions(+)
>  create mode 100644 drivers/i2c/muxes/i2c-mux-i2cs.c
> 
> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
> index f45a9cb..c95380d 100644
> --- a/drivers/i2c/muxes/Kconfig
> +++ b/drivers/i2c/muxes/Kconfig
> @@ -30,6 +30,16 @@ config I2C_MUX_GPIO
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called i2c-mux-gpio.
>  
> +config I2C_MUX_I2CS
> +	tristate "Juniper I2C Slave MFD client RE multiplexer"
> +	depends on MFD_JUNIPER_I2CS
> +	help
> +	  Select this to enable the Juniper I2C Slave RE multiplexer driver
> +	  on the relevant Juniper platforms.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called i2c-mux-i2cs.
> +
>  config I2C_MUX_PCA9541
>  	tristate "NXP PCA9541 I2C Master Selector"
>  	help
> diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
> index 78d8cba..45b4287 100644
> --- a/drivers/i2c/muxes/Makefile
> +++ b/drivers/i2c/muxes/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE)	+= i2c-arb-gpio-challenge.o
>  obj-$(CONFIG_I2C_DEMUX_PINCTRL)		+= i2c-demux-pinctrl.o
>  
>  obj-$(CONFIG_I2C_MUX_GPIO)	+= i2c-mux-gpio.o
> +obj-$(CONFIG_I2C_MUX_I2CS)	+= i2c-mux-i2cs.o
>  obj-$(CONFIG_I2C_MUX_PCA9541)	+= i2c-mux-pca9541.o
>  obj-$(CONFIG_I2C_MUX_PCA954x)	+= i2c-mux-pca954x.o
>  obj-$(CONFIG_I2C_MUX_PINCTRL)	+= i2c-mux-pinctrl.o
> diff --git a/drivers/i2c/muxes/i2c-mux-i2cs.c b/drivers/i2c/muxes/i2c-mux-i2cs.c

Please name the file i2c-mux-jnx-i2cs.c. Or something else a bit more
specific.

> new file mode 100644
> index 0000000..c498a44
> --- /dev/null
> +++ b/drivers/i2c/muxes/i2c-mux-i2cs.c
> @@ -0,0 +1,155 @@
> +/*
> + * Juniper Networks I2CS RE mux driver
> + *
> + * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/init.h>

init?

> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/regmap.h>

regmap?

> +#include <linux/platform_device.h>
> +#include <linux/mfd/jnx-i2cs-core.h>
> +
> +#define MISC_IO_RE_EN	0x01
> +
> +/*
> + * Read/write to mux register.
> + *   Don't use i2c_transfer()/i2c_smbus_xfer()
> + *   for this as they will try to lock adapter a second time
> + */

If this is the only concern, you should consider making this
gate mux-locked instead of parent-locked. Then you can avoid
all these unlocked games. But there are caveats, so you better
read Documentation/i2c/i2c-topology for the details before
you change.

> +static int i2cs_mux_read_byte(struct i2c_client *client,
> +				  u8 offset, u8 *val)
> +{
> +	struct i2c_msg msg[2];
> +
> +	msg[0].addr = client->addr;
> +	msg[0].flags = 0;
> +	msg[0].len = 1;
> +	msg[0].buf = &offset;
> +
> +	msg[1].addr = client->addr;
> +	msg[1].flags = I2C_M_RD;
> +	msg[1].len = 1;
> +	msg[1].buf = val;
> +
> +	return client->adapter->algo->master_xfer(client->adapter, msg, 2);

If you still want to be parent-locked, use __i2c_transfer. In
either case, consider adding code that handles the case of no
algo->master_xfer (some i2c adapters only support
algo->smbus_xfer).

> +}
> +
> +static int i2cs_mux_write_byte(struct i2c_client *client, u8 offset, u8 val)
> +{
> +	struct i2c_msg msg;
> +	char buf[2];
> +
> +	msg.addr = client->addr;
> +	msg.flags = 0;
> +	msg.len = 2;
> +	buf[0] = offset;
> +	buf[1] = val;
> +	msg.buf = buf;
> +
> +	return client->adapter->algo->master_xfer(client->adapter, &msg, 1);
> +}
> +
> +static int i2cs_mux_select(struct i2c_mux_core *muxc, u32 chan)
> +{
> +	int ret;
> +	u8 val = 0;
> +	struct i2c_client *client = i2c_mux_priv(muxc);
> +
> +	ret = i2cs_mux_read_byte(client, I2CS_MISC_IO, &val);
> +	if (ret < 0)
> +		return ret;
> +
> +	val |= MISC_IO_RE_EN;
> +	ret = i2cs_mux_write_byte(client, I2CS_MISC_IO, val);
> +

To me, it sounds as if I2CS_MISC_IO is a register with more than
one bit and that you are open to nasty races here. You probably
need to interact with the mfd parent and arrange some locking.

> +	return ret;
> +}
> +
> +static int i2cs_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
> +{
> +	int ret;
> +	u8 val = 0;
> +	struct i2c_client *client = i2c_mux_priv(muxc);
> +
> +	ret = i2cs_mux_read_byte(client, I2CS_MISC_IO, &val);
> +	if (ret < 0)
> +		return ret;
> +
> +	val &= ~MISC_IO_RE_EN;
> +	ret = i2cs_mux_write_byte(client, I2CS_MISC_IO, val);
> +
> +	return 0;
> +}
> +
> +static int i2cs_mux_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct i2c_client *client;
> +	struct i2c_mux_core *muxc;
> +	int ret;
> +
> +	if (!dev->parent)
> +		return -ENODEV;
> +
> +	client = i2c_verify_client(dev->parent);
> +	if (!client)
> +		return -ENODEV;
> +
> +	muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
> +				i2cs_mux_select, i2cs_mux_deselect);

You only have one child adapter, making this a gate. Please use
the I2C_MUX_GATE flag which should be available in 4.9. This also
affects the preferred devicetree, see comment on that patch.

> +	if (!muxc)
> +		return -ENOMEM;
> +	muxc->priv = client;
> +
> +	ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, muxc);
> +
> +	return 0;
> +}
> +
> +static int i2cs_mux_remove(struct platform_device *pdev)
> +{
> +	i2c_mux_del_adapters(platform_get_drvdata(pdev));
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id i2cs_mux_of_match[] = {
> +	{ .compatible = "jnx,i2c-mux-i2cs", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, i2cs_mux_of_match);
> +
> +static struct platform_driver i2cs_mux_driver = {
> +	.driver = {
> +		.name = "i2c-mux-i2cs",
> +		.owner  = THIS_MODULE,

Drop this line.

Cheers,
Peter

> +		.of_match_table = of_match_ptr(i2cs_mux_of_match),
> +	},
> +	.probe = i2cs_mux_probe,
> +	.remove = i2cs_mux_remove,
> +};
> +module_platform_driver(i2cs_mux_driver);
> +
> +MODULE_DESCRIPTION("Juniper Networks I2CS RE Mux driver");
> +MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:i2c-mux-i2cs");
> 

^ permalink raw reply

* Re: [PATCH 10/10] net: mdio-sam: Add device tree documentation for SAM MDIO
From: Peter Rosin @ 2016-10-10 14:53 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devicetree, Florian Fainelli,
	linux-watchdog, Georgi Vlaev, Wolfram Sang, Maryam Seraj,
	David Woodhouse, Linus Walleij, linux-kernel, linux-gpio,
	Wim Van Sebroeck, Rob Herring, linux-mtd, Debjit Ghosh, netdev,
	Brian Norris, Frank Rowand, Guenter Roeck, linux-i2c
In-Reply-To: <1475853518-22264-11-git-send-email-pantelis.antoniou@konsulko.com>

On 2016-10-07 17:18, Pantelis Antoniou wrote:
> From: Georgi Vlaev <gvlaev@juniper.net>
> 
> Add device tree bindings document for the SAM MDIO block
> present in Juniper's SAM FPGA.
> 
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
>  Documentation/devicetree/bindings/net/mdio-sam.txt | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/mdio-sam.txt
> 
> diff --git a/Documentation/devicetree/bindings/net/mdio-sam.txt b/Documentation/devicetree/bindings/net/mdio-sam.txt
> new file mode 100644
> index 0000000..7d354e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/mdio-sam.txt
> @@ -0,0 +1,48 @@
> +Juniper SAM FPGA MFD MDIO bus properties.
> +
> +Required properties:
> +- compatible : "jnx,mdio-sam"
> +- reg : The start offset of the MDIO bus range
> +- #address-cells = <1>;
> +- #size-cells = <0>;
> +
> +Optional properties:
> +
> +Required properties for child nodes:
> +- #address-cells = <1>;
> +- #size-cells = <0>;
> +- reg : The MDIO bus offset within the MDIO range.
> +
> +
> +Example :
> +
> +	sam@10 {
> +		compatible = "jnx,sam";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		mdio-sam@10 {

This should be mdio-sam@40000, OR ...

> +			compatible = "jnx,mdio-sam";
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			reg = <0x40000>;

... this should be reg = <0x10>. AFAIK.

Cheers,
Peter

> +
> +			mdio0: mdio-sam@0 {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				reg = <0x0>;
> +			};
> +
> +			mdio1: mdio-sam@4000 {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				reg = <0x4000>;
> +			};
> +
> +			mdio2: mdio-sam@8000 {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				reg = <0x8000>;
> +			};
> +		};
> +	};
> 


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply

* Re: [PATCHv3] hwmon: Add tc654 driver
From: Guenter Roeck @ 2016-10-10 13:21 UTC (permalink / raw)
  To: Chris Packham, linux-hwmon-u79uwXL29TY76Z2rM5mHXA
  Cc: iwamoto-sK/J6oeM9AhgKrQr38906+qrae++aQT8,
	Joshua.Scott-6g8wRflRTwXFdCa3tKVlE6U/zSkkHjvu, Kevin Tsai,
	Wolfram Sang, Rob Herring, Mark Rutland, Jean Delvare,
	Jonathan Corbet, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161009221222.29409-1-chris.packham-6g8wRflRTwXFdCa3tKVlE6U/zSkkHjvu@public.gmane.org>

On 10/09/2016 03:12 PM, Chris Packham wrote:
> Add support for the tc654 and tc655 fan controllers from Microchip.
>
> http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
>
> Signed-off-by: Chris Packham <chris.packham-6g8wRflRTwXFdCa3tKVlE6U/zSkkHjvu@public.gmane.org>
> ---
> Changes in v3:
> - typofix in documentation
> - add missing value to tc654_pwm_map, re-generate based on datasheet.
> - remove unnecessary hwmon_dev member from struct tc654_data
> - bug fixes in set_fan_min() and show_pwm_mode()
> - miscellaneous style fixes
>
> Changes in v2:
> - Add Documentation/hwmon/tc654
> - Incorporate most of the review comments from Guenter. Additional error
>   handling is added. Unused/unnecessary code is removed. I decided not
>   to go down the regmap path yet. I may circle back to it when I look at
>   using regmap in the adm9240 driver.
>
>  .../devicetree/bindings/i2c/trivial-devices.txt    |   2 +
>  Documentation/hwmon/tc654                          |  31 ++
>  drivers/hwmon/Kconfig                              |  11 +
>  drivers/hwmon/Makefile                             |   1 +
>  drivers/hwmon/tc654.c                              | 509 +++++++++++++++++++++
>  5 files changed, 554 insertions(+)
>  create mode 100644 Documentation/hwmon/tc654
>  create mode 100644 drivers/hwmon/tc654.c
>
> diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> index 1416c6a0d2cd..833fb9f133d3 100644
> --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> @@ -122,6 +122,8 @@ microchip,mcp4662-502	Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
>  microchip,mcp4662-103	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
>  microchip,mcp4662-503	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
>  microchip,mcp4662-104	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
> +microchip,tc654		PWM Fan Speed Controller With Fan Fault Detection
> +microchip,tc655		PWM Fan Speed Controller With Fan Fault Detection
>  national,lm63		Temperature sensor with integrated fan control
>  national,lm75		I2C TEMP SENSOR
>  national,lm80		Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
> diff --git a/Documentation/hwmon/tc654 b/Documentation/hwmon/tc654
> new file mode 100644
> index 000000000000..91a2843f5f98
> --- /dev/null
> +++ b/Documentation/hwmon/tc654
> @@ -0,0 +1,31 @@
> +Kernel driver tc654
> +===================
> +
> +Supported chips:
> +  * Microship TC654 and TC655
> +    Prefix: 'tc654'
> +    Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
> +
> +Authors:
> +        Chris Packham <chris.packham-6g8wRflRTwXFdCa3tKVlE6U/zSkkHjvu@public.gmane.org>
> +        Masahiko Iwamoto <iwamoto-sK/J6oeM9AhgKrQr38906+qrae++aQT8@public.gmane.org>
> +
> +Description
> +-----------
> +This driver implements support for the Microchip TC654 and TC655.
> +
> +The TC654 uses the 2-wire interface compatible with the SMBUS 2.0
> +specification. The TC654 has two (2) inputs for measuring fan RPM and
> +one (1) PWM output which can be used for fan control.
> +
> +Configuration Notes
> +-------------------
> +Ordinarily the pwm1_mode ABI is used for controlling the pwm output
> +mode.  However, for this chip the output is always pwm, and the
> +pwm1_mode determines if the pwm output is controlled via the pwm1 value
> +or via the Vin analog input.
> +
> +
> +Setting pwm1_mode to 1 will cause the pwm output to be driven based on
> +the pwm1 value. Setting pwm1_mode to 0 will cause the pwm output to be
> +driven based on the Vin input.
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 45cef3d2c75c..8681bc65cde5 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -907,6 +907,17 @@ config SENSORS_MCP3021
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called mcp3021.
>
> +config SENSORS_TC654
> +	tristate "Microchip TC654/TC655 and compatibles"
> +	depends on I2C
> +	help
> +	  If you say yes here you get support for TC654 and TC655.
> +	  The TC654 and TC655 are PWM mode fan speed controllers with
> +	  FanSense technology for use with brushless DC fans.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called tc654.
> +
>  config SENSORS_MENF21BMC_HWMON
>  	tristate "MEN 14F021P00 BMC Hardware Monitoring"
>  	depends on MFD_MENF21BMC
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index aecf4ba17460..c651f0f1d047 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_MAX6697)	+= max6697.o
>  obj-$(CONFIG_SENSORS_MAX31790)	+= max31790.o
>  obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>  obj-$(CONFIG_SENSORS_MCP3021)	+= mcp3021.o
> +obj-$(CONFIG_SENSORS_TC654)	+= tc654.o
>  obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
>  obj-$(CONFIG_SENSORS_NCT6683)	+= nct6683.o
>  obj-$(CONFIG_SENSORS_NCT6775)	+= nct6775.o
> diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c
> new file mode 100644
> index 000000000000..456e0bb9f94f
> --- /dev/null
> +++ b/drivers/hwmon/tc654.c
> @@ -0,0 +1,509 @@
> +/*
> + * tc654.c - Linux kernel modules for fan speed controller
> + *
> + * Copyright (C) 2016 Allied Telesis Labs NZ
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/jiffies.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/util_macros.h>
> +
> +enum tc654_regs {
> +	TC654_REG_RPM1 = 0x00,	/* RPM Output 1 */
> +	TC654_REG_RPM2 = 0x01,	/* RPM Output 2 */
> +	TC654_REG_FAN_FAULT1 = 0x02,	/* Fan Fault 1 Threshold */
> +	TC654_REG_FAN_FAULT2 = 0x03,	/* Fan Fault 2 Threshold */
> +	TC654_REG_CONFIG = 0x04,	/* Configuration */
> +	TC654_REG_STATUS = 0x05,	/* Status */
> +	TC654_REG_DUTY_CYCLE = 0x06,	/* Fan Speed Duty Cycle */
> +	TC654_REG_MFR_ID = 0x07,	/* Manufacturer Identification */
> +	TC654_REG_VER_ID = 0x08,	/* Version Identification */
> +};
> +
> +/* Macros to easily index the registers */
> +#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx))
> +#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx))
> +
> +/* Config register bits */
> +#define TC654_REG_CONFIG_RES BIT(6)	/* Resolution Selection */
> +#define TC654_REG_CONFIG_DUTYC BIT(5)	/* Duty Cycle Control Method */
> +#define TC654_REG_CONFIG_SDM BIT(0)	/* Shutdown Mode */
> +
> +/* Status register bits */
> +#define TC654_REG_STATUS_F2F BIT(1)	/* Fan 2 Fault */
> +#define TC654_REG_STATUS_F1F BIT(0)	/* Fan 1 Fault */
> +
> +/* RPM resolution for RPM Output registers */
> +#define TC654_HIGH_RPM_RESOLUTION 25	/* 25 RPM resolution */
> +#define TC654_LOW_RPM_RESOLUTION 50	/* 50 RPM resolution */
> +
> +/* Convert to the fan fault RPM threshold from register value */
> +#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50)	/* 50 RPM resolution */
> +
> +/* Convert to register value from the fan fault RPM threshold */
> +#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff)
> +

Nitpick, but the above defines are difficult to read. Can you tab-align the values ?

> +/* Register data is read (and cached) at most once per second. */
> +#define TC654_UPDATE_INTERVAL	HZ
> +
> +struct tc654_data {
> +	struct i2c_client *client;
> +
> +	/* update mutex */
> +	struct mutex update_lock;
> +
> +	/* tc654 register cache */
> +	bool valid;
> +	unsigned long last_updated;	/* in jiffies */
> +
> +	u8 rpm_output[2];	/* The fan RPM data for fans 1 and 2 is then
> +				 * written to registers RPM1 and RPM2
> +				 */
> +	u8 fan_fault[2];	/* The Fan Fault Threshold Registers are used to
> +				 * set the fan fault threshold levels for fan 1
> +				 * and fan 2
> +				 */
> +	u8 config;	/* The Configuration Register is an 8-bit read/
> +			 * writable multi-function control register
> +			 *   7: Fan Fault Clear
> +			 *      1 = Clear Fan Fault
> +			 *      0 = Normal Operation (default)
> +			 *   6: Resolution Selection for RPM Output Registers
> +			 *      RPM Output Registers (RPM1 and RPM2) will be
> +			 *      set for
> +			 *      1 = 25 RPM (9-bit) resolution
> +			 *      0 = 50 RPM (8-bit) resolution (default)
> +			 *   5: Duty Cycle Control Method
> +			 *      The V OUT duty cycle will be controlled via
> +			 *      1 = the SMBus interface.
> +			 *      0 = via the V IN analog input pin. (default)
> +			 * 4,3: Fan 2 Pulses Per Rotation
> +			 *      00 = 1
> +			 *      01 = 2 (default)
> +			 *      10 = 4
> +			 *      11 = 8
> +			 * 2,1: Fan 1 Pulses Per Rotation
> +			 *      00 = 1
> +			 *      01 = 2 (default)
> +			 *      10 = 4
> +			 *      11 = 8
> +			 *   0: Shutdown Mode
> +			 *      1 = Shutdown mode.
> +			 *      0 = Normal operation. (default)
> +			 */
> +	u8 status;	/* The Status register provides all the information
> +			 * about what is going on within the TC654/TC655
> +			 * devices.
> +			 * 7,6: Unimplemented, Read as '0'
> +			 *   5: Over-Temperature Fault Condition
> +			 *      1 = Over-Temperature condition has occurred
> +			 *      0 = Normal operation. V IN is less than 2.6V
> +			 *   4: RPM2 Counter Overflow
> +			 *      1 = Fault condition
> +			 *      0 = Normal operation
> +			 *   3: RPM1 Counter Overflow
> +			 *      1 = Fault condition
> +			 *      0 = Normal operation
> +			 *   2: V IN Input Status
> +			 *      1 = V IN is open
> +			 *      0 = Normal operation. voltage present at V IN
> +			 *   1: Fan 2 Fault
> +			 *      1 = Fault condition
> +			 *      0 = Normal operation
> +			 *   0: Fan 1 Fault
> +			 *      1 = Fault condition
> +			 *      0 = Normal operation
> +			 */
> +	u8 duty_cycle;	/* The DUTY_CYCLE register is a 4-bit read/
> +			 * writable register used to control the duty
> +			 * cycle of the V OUT output.
> +			 */
> +};
> +
> +/* helper to grab and cache data, at most one time per second */
> +static struct tc654_data *tc654_update_client(struct device *dev)
> +{
> +	struct tc654_data *data = dev_get_drvdata(dev);
> +	struct i2c_client *client = data->client;
> +	int ret = 0;
> +
> +	mutex_lock(&data->update_lock);
> +	if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) &&
> +	    likely(data->valid))
> +		goto out;
> +
> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0));
> +	if (ret < 0)
> +		goto out;
> +	data->rpm_output[0] = ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1));
> +	if (ret < 0)
> +		goto out;
> +	data->rpm_output[1] = ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0));
> +	if (ret < 0)
> +		goto out;
> +	data->fan_fault[0] = ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1));
> +	if (ret < 0)
> +		goto out;
> +	data->fan_fault[1] = ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
> +	if (ret < 0)
> +		goto out;
> +	data->config = ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS);
> +	if (ret < 0)
> +		goto out;
> +	data->status = ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE);
> +	if (ret < 0)
> +		goto out;
> +	data->duty_cycle = ret & 0x0f;
> +
> +	data->last_updated = jiffies;
> +	data->valid = true;
> +out:
> +	mutex_unlock(&data->update_lock);
> +
> +	if (ret < 0)		/* upon error, encode it in return value */
> +		data = ERR_PTR(ret);
> +
> +	return data;
> +}
> +
> +/*
> + * sysfs attributes
> + */
> +
> +static ssize_t show_fan(struct device *dev, struct device_attribute *da,
> +			char *buf)
> +{
> +	int nr = to_sensor_dev_attr(da)->index;
> +	struct tc654_data *data = tc654_update_client(dev);
> +	int val;
> +
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	if (data->config & TC654_REG_CONFIG_RES)
> +		val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION;
> +	else
> +		val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION;
> +
> +	return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
> +			    char *buf)
> +{
> +	int nr = to_sensor_dev_attr(da)->index;
> +	struct tc654_data *data = tc654_update_client(dev);
> +
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	return sprintf(buf, "%d\n",
> +		       TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr]));
> +}
> +
> +static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
> +			   const char *buf, size_t count)
> +{
> +	int nr = to_sensor_dev_attr(da)->index;
> +	struct tc654_data *data = dev_get_drvdata(dev);
> +	struct i2c_client *client = data->client;
> +	unsigned long val;
> +	int ret;
> +
> +	if (kstrtoul(buf, 10, &val))
> +		return -EINVAL;
> +
> +	val = clamp_val(val, 0, 12750);
> +
> +	mutex_lock(&data->update_lock);
> +
> +	data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val);
> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr),
> +					data->fan_fault[nr]);
> +
> +	mutex_unlock(&data->update_lock);
> +	return ret < 0 ? ret : count;
> +}
> +
> +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da,
> +			      char *buf)
> +{
> +	int nr = to_sensor_dev_attr(da)->index;
> +	struct tc654_data *data = tc654_update_client(dev);
> +	int val;
> +
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	if (nr == 0)
> +		val = !!(data->status & TC654_REG_STATUS_F1F);
> +	else
> +		val = !!(data->status & TC654_REG_STATUS_F2F);
> +
> +	return sprintf(buf, "%d\n", val);
> +}
> +
> +static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 };
> +
> +static ssize_t show_fan_pulses(struct device *dev, struct device_attribute *da,
> +			      char *buf)
> +{
> +	int nr = to_sensor_dev_attr(da)->index;
> +	struct tc654_data *data = tc654_update_client(dev);
> +	u8 val;
> +
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03);
> +	return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t set_fan_pulses(struct device *dev, struct device_attribute *da,
> +			     const char *buf, size_t count)
> +{
> +	int nr = to_sensor_dev_attr(da)->index;
> +	struct tc654_data *data = dev_get_drvdata(dev);
> +	struct i2c_client *client = data->client;
> +	u8 config;
> +	unsigned long val;
> +	int ret;
> +
> +	if (kstrtoul(buf, 10, &val))
> +		return -EINVAL;
> +
> +	switch (val) {
> +	case 1:
> +		config = 0;
> +		break;
> +	case 2:
> +		config = 1;
> +		break;
> +	case 4:
> +		config = 2;
> +		break;
> +	case 8:
> +		config = 3;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&data->update_lock);
> +
> +	data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]);
> +	data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]);
> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> +
> +	mutex_unlock(&data->update_lock);
> +	return ret < 0 ? ret : count;
> +}
> +
> +static ssize_t show_pwm_mode(struct device *dev,
> +			     struct device_attribute *da, char *buf)
> +{
> +	struct tc654_data *data = tc654_update_client(dev);
> +
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	return sprintf(buf, "%d\n", !!(data->config & TC654_REG_CONFIG_DUTYC));
> +}
> +
> +static ssize_t set_pwm_mode(struct device *dev,
> +			    struct device_attribute *da,
> +			    const char *buf, size_t count)
> +{
> +	struct tc654_data *data = dev_get_drvdata(dev);
> +	struct i2c_client *client = data->client;
> +	unsigned long val;
> +	int ret;
> +
> +	if (kstrtoul(buf, 10, &val))
> +		return -EINVAL;
> +
> +	if (val != 0 && val != 1)
> +		return -EINVAL;
> +
> +	mutex_lock(&data->update_lock);
> +
> +	if (val)
> +		data->config |= TC654_REG_CONFIG_DUTYC;
> +	else
> +		data->config &= ~TC654_REG_CONFIG_DUTYC;

I just realized that this won't work as intended. Problem is that you
only fill data->config when reading an attribute. So, if a set function
is called prior to reading an attribute, data->config will be 0, and
you end up overwriting the original configuration.

> +
> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> +
> +	mutex_unlock(&data->update_lock);
> +	return ret < 0 ? ret : count;
> +}
> +
> +static const int tc654_pwm_map[16] = { 77,  88, 102, 112, 124, 136, 148, 160,
> +				      172, 184, 196, 207, 219, 231, 243, 255};
> +
> +static ssize_t show_pwm(struct device *dev, struct device_attribute *da,
> +			       char *buf)
> +{
> +	struct tc654_data *data = tc654_update_client(dev);
> +	int pwm;
> +
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	if (data->config & TC654_REG_CONFIG_SDM)
> +		pwm = 0;
> +	else
> +		pwm = tc654_pwm_map[data->duty_cycle];
> +
> +	return sprintf(buf, "%d\n", pwm);
> +}
> +
> +static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
> +			      const char *buf, size_t count)
> +{
> +	struct tc654_data *data = dev_get_drvdata(dev);
> +	struct i2c_client *client = data->client;
> +	unsigned long val;
> +	int ret;
> +
> +	if (kstrtoul(buf, 10, &val))
> +		return -EINVAL;
> +	if (val > 255)
> +		return -EINVAL;
> +
> +	if (val == 0)
> +		data->config |= TC654_REG_CONFIG_SDM;
> +	else
> +		data->config &= ~TC654_REG_CONFIG_SDM;
> +
> +	data->duty_cycle = find_closest(val, tc654_pwm_map,
> +					ARRAY_SIZE(tc654_pwm_map));
> +
> +	mutex_lock(&data->update_lock);
> +
The lock has to be earlier, before writing duty_cycle.

> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE,
> +				  data->duty_cycle);
> +	if (ret < 0)
> +		goto out;

This goto is not necessary.

> +
> +out:
> +	mutex_unlock(&data->update_lock);
> +	return ret < 0 ? ret : count;
> +}
> +
> +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
> +			  set_fan_min, 0);
> +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
> +			  set_fan_min, 1);
> +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
> +			  set_fan_pulses, 0);
> +static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
> +			  set_fan_pulses, 1);
> +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
> +			  show_pwm_mode, set_pwm_mode, 0);
> +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm,
> +			  set_pwm, 0);
> +
> +/* Driver data */
> +static struct attribute *tc654_attrs[] = {
> +	&sensor_dev_attr_fan1_input.dev_attr.attr,
> +	&sensor_dev_attr_fan2_input.dev_attr.attr,
> +	&sensor_dev_attr_fan1_min.dev_attr.attr,
> +	&sensor_dev_attr_fan2_min.dev_attr.attr,
> +	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
> +	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
> +	&sensor_dev_attr_fan1_pulses.dev_attr.attr,
> +	&sensor_dev_attr_fan2_pulses.dev_attr.attr,
> +	&sensor_dev_attr_pwm1_mode.dev_attr.attr,
> +	&sensor_dev_attr_pwm1.dev_attr.attr,
> +	NULL
> +};
> +
> +ATTRIBUTE_GROUPS(tc654);
> +
> +/*
> + * device probe and removal
> + */
> +
> +static int tc654_probe(struct i2c_client *client,
> +		       const struct i2c_device_id *id)
> +{
> +	struct device *dev = &client->dev;
> +	struct tc654_data *data;
> +	struct device *hwmon_dev;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> +		return -ENODEV;
> +
> +	data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->client = client;
> +	mutex_init(&data->update_lock);
> +
> +	hwmon_dev =
> +	    devm_hwmon_device_register_with_groups(dev, client->name, data,
> +						   tc654_groups);
> +	return PTR_ERR_OR_ZERO(hwmon_dev);
> +}
> +
> +static const struct i2c_device_id tc654_id[] = {
> +	{"tc654", 0},
> +	{"tc655", 0},
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, tc654_id);
> +
> +static struct i2c_driver tc654_driver = {
> +	.driver = {
> +		   .name = "tc654",
> +		   },
> +	.probe = tc654_probe,
> +	.id_table = tc654_id,
> +};
> +
> +module_i2c_driver(tc654_driver);
> +
> +MODULE_AUTHOR("Allied Telesis Labs");
> +MODULE_DESCRIPTION("Microchip TC654/TC655 driver");
> +MODULE_LICENSE("GPL");
>

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [GIT PULL] i2c: 'i2c-bus' node support for v4.8-rc1
From: Jon Hunter @ 2016-10-10 12:46 UTC (permalink / raw)
  To: Wolfram Sang, Thierry Reding
  Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20160701154430.GA1372@tetsubishi>

Hi Wolfram,

On 01/07/16 16:44, Wolfram Sang wrote:
> * PGP Signed by an unknown key
> 
> On Fri, Jul 01, 2016 at 05:00:55PM +0200, Thierry Reding wrote:
>> Hi Wolfram,
>>
>> The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
>>
>>   Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)
>>
>> are available in the git repository at:
>>
>>   git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git tags/tegra-for-4.8-i2c
>>
>> for you to fetch changes up to 7e4c224abfe8e2a00f56a2ef0198e6de3ca1852c:
>>
>>   i2c: core: Add support for 'i2c-bus' subnode (2016-06-30 11:02:17 +0200)
> 
> Thanks, pulled.

I was just checking the latest mainline and -next and I did not see the
changes for the 'i2c-bus' node. Let me know if I have missed an email
somewhere or if these were dropped for some reason.

Cheers
Jon

-- 
nvpublic

^ permalink raw reply

* Re: [PATCH 08/10] leds: Add binding for Juniper's I2CS FPGA
From: Jacek Anaszewski @ 2016-10-10  9:41 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Wolfram Sang, Richard Purdie, Jean Delvare,
	Peter Rosin, Avirup Banerjee, Georgi Vlaev, Guenter Roeck,
	JawaharBalaji Thirumalaisamy, devicetree, linux-kernel,
	linux-gpio, linux-i2c, linux-leds, linux-hwmon
In-Reply-To: <1475853669-22480-9-git-send-email-pantelis.antoniou@konsulko.com>

Hi Pantelis,

On 10/07/2016 05:21 PM, Pantelis Antoniou wrote:
> From: Georgi Vlaev <gvlaev@juniper.net>
>
> Document bindings for the I2CS FPGA leds.
>
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
>  .../devicetree/bindings/leds/leds-i2cs.txt         | 34 ++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/leds/leds-i2cs.txt
>
> diff --git a/Documentation/devicetree/bindings/leds/leds-i2cs.txt b/Documentation/devicetree/bindings/leds/leds-i2cs.txt
> new file mode 100644
> index 0000000..100e584
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/leds-i2cs.txt
> @@ -0,0 +1,34 @@
> +Juniper I2CS LED driver.
> +
> +This is present in Juniper platforms that use a I2CS Slave FPGA.
> +
> +Required properties:
> +  - compatible: must be "jnx,leds-i2cs"

s/compatible:/compatible :/

Also treat the description as a regular sentence, i.e. begin it
with a capital letter and put a dot at the end.

> +  - #address-cells : must be 1.
> +  - #size-cells : must be 0.

s/must/Must/

> +Each led is represented as a sub-node of the jnx,leds-i2cs device.
> +
> +LED sub-node properties:
> +- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
> +- reg : number of LED
> +- linux,default-trigger : (optional)
> +   see Documentation/devicetree/bindings/leds/common.txt

Driver uses also "hw-blink" property. Please document it here, but
add also a prefix:

jnx,hw-blink

Also "jnx" entry should be added to 
Documentation/devicetree/bindings/vendor-prefixes.txt.

> +Example:
> +
> +leds_fpc0: leds-jnx-i2cs {
> +	compatible = "jnx,leds-i2cs";
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +
> +	fpc0-fail {
> +		reg = <1>;

It would be good to provide also an example of "label"
property to suggest LED class device name according to
the LED class device naming convention.
See Documentation/leds/leds-class.txt for details.

> +		linux,default-trigger = "fpc0-fail";
> +	};
> +
> +	fpc0-ok {
> +		reg = <2>;
> +		linux,default-trigger = "fpc0-ok";
> +	};
> +};
>


-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply

* Re: [PATCH 07/10] leds: i2cs: Add I2CS FPGA leds driver
From: Jacek Anaszewski @ 2016-10-10  9:41 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Wolfram Sang, Richard Purdie, Jean Delvare,
	Peter Rosin, Avirup Banerjee, Georgi Vlaev, Guenter Roeck,
	JawaharBalaji Thirumalaisamy, devicetree, linux-kernel,
	linux-gpio, linux-i2c, linux-leds, linux-hwmon
In-Reply-To: <1475853669-22480-8-git-send-email-pantelis.antoniou@konsulko.com>

Hi Pantelis,

Thanks for the patch. Please find my comments in the code below.

On 10/07/2016 05:21 PM, Pantelis Antoniou wrote:
> From: Georgi Vlaev <gvlaev@juniper.net>
>
> Add support for the FRU faceplate status LEDs (OK, FAIL,
> ACTIVE, STANDBY) controlled by the Juniper I2CS FPGA. This
> driver is a jnx-i2cs-core client.
>
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
>  drivers/leds/Kconfig         |   9 ++
>  drivers/leds/Makefile        |   1 +
>  drivers/leds/leds-jnx-i2cs.c | 219 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 229 insertions(+)
>  create mode 100644 drivers/leds/leds-jnx-i2cs.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 7a628c6..45c6612 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -659,6 +659,15 @@ config LEDS_MLXCPLD
>  	  This option enabled support for the LEDs on the Mellanox
>  	  boards. Say Y to enabled these.
>
> +config LEDS_JNX_I2CS
> +	tristate "LED support for the Juniper Networks I2CS FPGA"
> +	depends on LEDS_CLASS && I2C
> +	select REGMAP_I2C
> +	help
> +	  This option enables support for the FRU faceplate status
> +	  LEDs (OK, FAIL, ACTIVE, STANDBY) controlled by the Juniper
> +	  I2CS FPGA.
> +
>  comment "LED Triggers"
>  source "drivers/leds/trigger/Kconfig"
>
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index 3965070..1ce2d0b 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -71,6 +71,7 @@ obj-$(CONFIG_LEDS_IS31FL319X)		+= leds-is31fl319x.o
>  obj-$(CONFIG_LEDS_IS31FL32XX)		+= leds-is31fl32xx.o
>  obj-$(CONFIG_LEDS_PM8058)		+= leds-pm8058.o
>  obj-$(CONFIG_LEDS_MLXCPLD)		+= leds-mlxcpld.o
> +obj-$(CONFIG_LEDS_JNX_I2CS)		+= leds-jnx-i2cs.o
>
>  # LED SPI Drivers
>  obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
> diff --git a/drivers/leds/leds-jnx-i2cs.c b/drivers/leds/leds-jnx-i2cs.c
> new file mode 100644
> index 0000000..c2d7274
> --- /dev/null
> +++ b/drivers/leds/leds-jnx-i2cs.c
> @@ -0,0 +1,219 @@
> +/*
> + * Juniper Networks I2CS FPGA LEDs driver
> + *
> + * Copyright (C) 2016 Juniper Networks
> + * Author: Georgi Vlaev <gvlaev@juniper.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/of.h>
> +#include <linux/delay.h>
> +#include <linux/leds.h>
> +#include <linux/i2c.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/jnx-i2cs-core.h>

Please arrange include directives in alphabetical order.

> +#define FRU_LEDS	4 /* Total LEDs (active, fail, ok, standby) */
> +#define HW_BLINK_LEDS	3 /* LEDs with hw blink cap (standby not supported) */
> +
> +/*
> + * I2CS fru_led [0x12]
> + *
> + * bit 6   | bit 5    | bit 4   |...                        | bit 0
> + * blink_ok|blink_fail|blink_act|led_standby|led_ok|led_fail|led_act
> + */
> +
> +/* TODO: Use the regmap from the parent MFD */

Isn't it a good moment to address that?

> +static struct regmap_config i2cs_leds_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = I2CS_SPARE_OE,
> +};
> +
> +struct i2cs_led {
> +	struct led_classdev lc;

Most drivers use led_cdev or cdev name for it.
It is more informative and improves readability.

> +	struct regmap *regmap;
> +	struct work_struct work;
> +	int blink;
> +	int on;
> +	int bit;
> +};
> +
> +struct i2cs_led_data {
> +	int num_leds;
> +	struct i2cs_led *leds;
> +};
> +
> +static void jnx_i2cs_leds_work(struct work_struct *work)
> +{
> +	struct i2cs_led *led = container_of(work, struct i2cs_led, work);
> +
> +	int mask = (BIT(led->bit) << 4) | BIT(led->bit);
> +	int value = ((led->blink << led->bit) << 4) | (led->on << led->bit);
> +
> +	regmap_update_bits(led->regmap, I2CS_FRU_LED, mask, value & 0x7f);
> +}

If you used brightness_set_blocking op instead of brightness_set,
then you could get rid of the in-driver work queue.

> +
> +static void jnx_i2cs_leds_brightness_set(struct led_classdev *lc,
> +				 enum led_brightness brightness)
> +{
> +	struct i2cs_led *led = container_of(lc, struct i2cs_led, lc);
> +
> +	led->on = (brightness != LED_OFF);
> +	led->blink = 0; /* always turn off hw blink on brightness_set() */

Some time ago we changed brightness setting semantics when blinking
is on. Now setting any brightness > 0 shouldn't disable blinking.
Following commit provides the details:

7cfe749fad51 ("leds: core: Fix brightness setting upon hardware blinking 
enabled")

It seems that the hardware supports only one brightness level.
In such a case I'd return immediately if the LED is on and
brightness to be set is 1.

> +	schedule_work(&led->work);
> +}
> +
> +static int jnx_i2cs_leds_blink_set(struct led_classdev *lc,
> +				   unsigned long *delay_on,
> +				   unsigned long *delay_off)
> +{
> +	struct i2cs_led *led = container_of(lc, struct i2cs_led, lc);
> +
> +	led->blink = (*delay_on > 0);

blink_set op should fail if hardware doesn't support delay_on
or delay_off periods. I lets the LED core to apply software blink
fallback.

> +	led->on = led->blink; /* 'on' bit should be set if blinking */
> +	schedule_work(&led->work);
> +
> +	return 0;
> +}
> +
> +static int jnx_i2cs_leds_init_one(struct device *dev, struct device_node *np,
> +				  struct i2cs_led_data *ild,
> +				  struct regmap *regmap, int num)
> +{
> +	struct i2cs_led *led;
> +	const char *string;
> +	bool hw_blink;
> +	int ret;
> +	u32 reg;
> +
> +	ret = of_property_read_u32(np, "reg", &reg);
> +	if (ret || reg >= FRU_LEDS)
> +		return -ENODEV;
> +
> +	led = &ild->leds[num];
> +	led->bit = reg;
> +	led->regmap = regmap;
> +
> +	if (!of_property_read_string(np, "label", &string))
> +		led->lc.name = string;
> +	else
> +		led->lc.name = np->name;
> +
> +	if (!of_property_read_string(np, "linux,default-trigger", &string))
> +		led->lc.default_trigger = string;
> +
> +	led->lc.brightness = LED_OFF;
> +	led->lc.brightness_set = jnx_i2cs_leds_brightness_set;

You need also:

led->lc.max_brightness = 1;

> +	if (led->bit <= HW_BLINK_LEDS) {
> +		hw_blink = of_property_read_bool(np, "hw-blink");
> +		if (hw_blink)
> +			led->lc.blink_set = jnx_i2cs_leds_blink_set;
> +	}
> +
> +	ret = devm_led_classdev_register(dev, &led->lc);
> +	if (ret)
> +		return ret;
> +
> +	INIT_WORK(&led->work, jnx_i2cs_leds_work);
> +
> +	return 0;
> +}
> +
> +static int jnx_i2cs_leds_of_init(struct device *dev, struct i2cs_led_data *ild)
> +{
> +	struct device_node *child, *np = dev->of_node;
> +	struct regmap *regmap;
> +	struct i2c_client *client;
> +	int ret, num_leds, i = 0;
> +
> +	if (!dev->parent)
> +		return -ENODEV;
> +
> +	client = i2c_verify_client(dev->parent);
> +	if (!client)
> +		return -ENODEV;
> +
> +	regmap = devm_regmap_init_i2c(client, &i2cs_leds_regmap_config);
> +	if (IS_ERR(regmap)) {
> +		dev_err(dev, "Failed to allocate register map\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	num_leds = of_get_child_count(np);
> +	if (!num_leds || num_leds > FRU_LEDS)
> +		return -ENODEV;
> +
> +	ild->num_leds = num_leds;
> +	ild->leds = devm_kzalloc(dev, sizeof(struct i2cs_led) * num_leds,
> +				 GFP_KERNEL);
> +	if (!ild->leds)
> +		return -ENOMEM;
> +
> +	for_each_child_of_node(np, child) {
> +		ret = jnx_i2cs_leds_init_one(dev, child, ild, regmap, i++);
> +		if (ret)
> +			return ret;

of_node_put(child) should be called on error to avoid memory leak.

> +	}
> +
> +	return 0;
> +}
> +
> +static int jnx_i2cs_leds_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct i2cs_led_data *ild;
> +	int ret;
> +
> +	ild = devm_kzalloc(dev, sizeof(*ild), GFP_KERNEL);
> +	if (!ild)
> +		return -ENOMEM;
> +
> +	ret = jnx_i2cs_leds_of_init(dev, ild);
> +	if (ret < 0)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, ild);
> +
> +	return 0;
> +}
> +
> +static int jnx_i2cs_leds_remove(struct platform_device *pdev)
> +{
> +	struct i2cs_led_data *ild = platform_get_drvdata(pdev);
> +	int i;
> +
> +	for (i = 0; i < ild->num_leds; i++) {
> +		devm_led_classdev_unregister(&pdev->dev, &ild->leds[i].lc);

This is redundant.

> +		cancel_work_sync(&ild->leds[i].work);
> +	}
> +
> +	return 0;
> +}

This function will be redundant after removing work queue.

> +static const struct of_device_id jnx_i2cs_leds_match[] = {
> +	{ .compatible = "jnx,leds-i2cs", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, jnx_i2cs_leds_match);
> +
> +static struct platform_driver jnx_i2cs_leds_driver = {
> +	.driver = {
> +		.name  = "leds-i2cs",
> +		.of_match_table = jnx_i2cs_leds_match,
> +	},
> +	.probe = jnx_i2cs_leds_probe,
> +	.remove = jnx_i2cs_leds_remove,
> +};
> +
> +module_platform_driver(jnx_i2cs_leds_driver);
> +
> +MODULE_DESCRIPTION("Juniper Networks I2CS leds driver");
> +MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
> +MODULE_LICENSE("GPL");
>


-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply

* Re: [PATCH 10/10] net: mdio-sam: Add device tree documentation for SAM MDIO
From: Florian Fainelli @ 2016-10-10  8:50 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Wolfram Sang, David Woodhouse, Brian Norris,
	Wim Van Sebroeck, Peter Rosin, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, Maryam Seraj, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1475853518-22264-11-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>



On 10/07/2016 08:18 AM, Pantelis Antoniou wrote:
> From: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
> 
> Add device tree bindings document for the SAM MDIO block
> present in Juniper's SAM FPGA.
> 
> Signed-off-by: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/net/mdio-sam.txt | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/mdio-sam.txt
> 
> diff --git a/Documentation/devicetree/bindings/net/mdio-sam.txt b/Documentation/devicetree/bindings/net/mdio-sam.txt
> new file mode 100644
> index 0000000..7d354e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/mdio-sam.txt
> @@ -0,0 +1,48 @@
> +Juniper SAM FPGA MFD MDIO bus properties.
> +
> +Required properties:
> +- compatible : "jnx,mdio-sam"
> +- reg : The start offset of the MDIO bus range
> +- #address-cells = <1>;
> +- #size-cells = <0>;
> +
> +Optional properties:
> +
> +Required properties for child nodes:
> +- #address-cells = <1>;
> +- #size-cells = <0>;
> +- reg : The MDIO bus offset within the MDIO range.

I would just refer to Documentation/devicetree/bindings/net/phy.txt for
the child node properties.

Other than that:

Reviewed-by: Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
-- 
Florian
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCHv3] hwmon: Add tc654 driver
From: Chris Packham @ 2016-10-09 22:12 UTC (permalink / raw)
  To: linux, linux-hwmon
  Cc: iwamoto, Joshua.Scott, Chris Packham, Kevin Tsai, Wolfram Sang,
	Rob Herring, Mark Rutland, Jean Delvare, Jonathan Corbet,
	linux-i2c, devicetree, linux-kernel, linux-doc
In-Reply-To: <20161007182939.GA9031@roeck-us.net>

Add support for the tc654 and tc655 fan controllers from Microchip.

http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Changes in v3:
- typofix in documentation
- add missing value to tc654_pwm_map, re-generate based on datasheet.
- remove unnecessary hwmon_dev member from struct tc654_data
- bug fixes in set_fan_min() and show_pwm_mode()
- miscellaneous style fixes
    
Changes in v2:
- Add Documentation/hwmon/tc654
- Incorporate most of the review comments from Guenter. Additional error
  handling is added. Unused/unnecessary code is removed. I decided not
  to go down the regmap path yet. I may circle back to it when I look at
  using regmap in the adm9240 driver.

 .../devicetree/bindings/i2c/trivial-devices.txt    |   2 +
 Documentation/hwmon/tc654                          |  31 ++
 drivers/hwmon/Kconfig                              |  11 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/tc654.c                              | 509 +++++++++++++++++++++
 5 files changed, 554 insertions(+)
 create mode 100644 Documentation/hwmon/tc654
 create mode 100644 drivers/hwmon/tc654.c

diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index 1416c6a0d2cd..833fb9f133d3 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -122,6 +122,8 @@ microchip,mcp4662-502	Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
 microchip,mcp4662-103	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
 microchip,mcp4662-503	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
 microchip,mcp4662-104	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
+microchip,tc654		PWM Fan Speed Controller With Fan Fault Detection
+microchip,tc655		PWM Fan Speed Controller With Fan Fault Detection
 national,lm63		Temperature sensor with integrated fan control
 national,lm75		I2C TEMP SENSOR
 national,lm80		Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
diff --git a/Documentation/hwmon/tc654 b/Documentation/hwmon/tc654
new file mode 100644
index 000000000000..91a2843f5f98
--- /dev/null
+++ b/Documentation/hwmon/tc654
@@ -0,0 +1,31 @@
+Kernel driver tc654
+===================
+
+Supported chips:
+  * Microship TC654 and TC655
+    Prefix: 'tc654'
+    Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
+
+Authors:
+        Chris Packham <chris.packham@alliedtelesis.co.nz>
+        Masahiko Iwamoto <iwamoto@allied-telesis.co.jp>
+
+Description
+-----------
+This driver implements support for the Microchip TC654 and TC655.
+
+The TC654 uses the 2-wire interface compatible with the SMBUS 2.0
+specification. The TC654 has two (2) inputs for measuring fan RPM and
+one (1) PWM output which can be used for fan control.
+
+Configuration Notes
+-------------------
+Ordinarily the pwm1_mode ABI is used for controlling the pwm output
+mode.  However, for this chip the output is always pwm, and the
+pwm1_mode determines if the pwm output is controlled via the pwm1 value
+or via the Vin analog input.
+
+
+Setting pwm1_mode to 1 will cause the pwm output to be driven based on
+the pwm1 value. Setting pwm1_mode to 0 will cause the pwm output to be
+driven based on the Vin input.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 45cef3d2c75c..8681bc65cde5 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -907,6 +907,17 @@ config SENSORS_MCP3021
 	  This driver can also be built as a module.  If so, the module
 	  will be called mcp3021.
 
+config SENSORS_TC654
+	tristate "Microchip TC654/TC655 and compatibles"
+	depends on I2C
+	help
+	  If you say yes here you get support for TC654 and TC655.
+	  The TC654 and TC655 are PWM mode fan speed controllers with
+	  FanSense technology for use with brushless DC fans.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called tc654.
+
 config SENSORS_MENF21BMC_HWMON
 	tristate "MEN 14F021P00 BMC Hardware Monitoring"
 	depends on MFD_MENF21BMC
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index aecf4ba17460..c651f0f1d047 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_MAX6697)	+= max6697.o
 obj-$(CONFIG_SENSORS_MAX31790)	+= max31790.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MCP3021)	+= mcp3021.o
+obj-$(CONFIG_SENSORS_TC654)	+= tc654.o
 obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
 obj-$(CONFIG_SENSORS_NCT6683)	+= nct6683.o
 obj-$(CONFIG_SENSORS_NCT6775)	+= nct6775.o
diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c
new file mode 100644
index 000000000000..456e0bb9f94f
--- /dev/null
+++ b/drivers/hwmon/tc654.c
@@ -0,0 +1,509 @@
+/*
+ * tc654.c - Linux kernel modules for fan speed controller
+ *
+ * Copyright (C) 2016 Allied Telesis Labs NZ
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/util_macros.h>
+
+enum tc654_regs {
+	TC654_REG_RPM1 = 0x00,	/* RPM Output 1 */
+	TC654_REG_RPM2 = 0x01,	/* RPM Output 2 */
+	TC654_REG_FAN_FAULT1 = 0x02,	/* Fan Fault 1 Threshold */
+	TC654_REG_FAN_FAULT2 = 0x03,	/* Fan Fault 2 Threshold */
+	TC654_REG_CONFIG = 0x04,	/* Configuration */
+	TC654_REG_STATUS = 0x05,	/* Status */
+	TC654_REG_DUTY_CYCLE = 0x06,	/* Fan Speed Duty Cycle */
+	TC654_REG_MFR_ID = 0x07,	/* Manufacturer Identification */
+	TC654_REG_VER_ID = 0x08,	/* Version Identification */
+};
+
+/* Macros to easily index the registers */
+#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx))
+#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx))
+
+/* Config register bits */
+#define TC654_REG_CONFIG_RES BIT(6)	/* Resolution Selection */
+#define TC654_REG_CONFIG_DUTYC BIT(5)	/* Duty Cycle Control Method */
+#define TC654_REG_CONFIG_SDM BIT(0)	/* Shutdown Mode */
+
+/* Status register bits */
+#define TC654_REG_STATUS_F2F BIT(1)	/* Fan 2 Fault */
+#define TC654_REG_STATUS_F1F BIT(0)	/* Fan 1 Fault */
+
+/* RPM resolution for RPM Output registers */
+#define TC654_HIGH_RPM_RESOLUTION 25	/* 25 RPM resolution */
+#define TC654_LOW_RPM_RESOLUTION 50	/* 50 RPM resolution */
+
+/* Convert to the fan fault RPM threshold from register value */
+#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50)	/* 50 RPM resolution */
+
+/* Convert to register value from the fan fault RPM threshold */
+#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff)
+
+/* Register data is read (and cached) at most once per second. */
+#define TC654_UPDATE_INTERVAL	HZ
+
+struct tc654_data {
+	struct i2c_client *client;
+
+	/* update mutex */
+	struct mutex update_lock;
+
+	/* tc654 register cache */
+	bool valid;
+	unsigned long last_updated;	/* in jiffies */
+
+	u8 rpm_output[2];	/* The fan RPM data for fans 1 and 2 is then
+				 * written to registers RPM1 and RPM2
+				 */
+	u8 fan_fault[2];	/* The Fan Fault Threshold Registers are used to
+				 * set the fan fault threshold levels for fan 1
+				 * and fan 2
+				 */
+	u8 config;	/* The Configuration Register is an 8-bit read/
+			 * writable multi-function control register
+			 *   7: Fan Fault Clear
+			 *      1 = Clear Fan Fault
+			 *      0 = Normal Operation (default)
+			 *   6: Resolution Selection for RPM Output Registers
+			 *      RPM Output Registers (RPM1 and RPM2) will be
+			 *      set for
+			 *      1 = 25 RPM (9-bit) resolution
+			 *      0 = 50 RPM (8-bit) resolution (default)
+			 *   5: Duty Cycle Control Method
+			 *      The V OUT duty cycle will be controlled via
+			 *      1 = the SMBus interface.
+			 *      0 = via the V IN analog input pin. (default)
+			 * 4,3: Fan 2 Pulses Per Rotation
+			 *      00 = 1
+			 *      01 = 2 (default)
+			 *      10 = 4
+			 *      11 = 8
+			 * 2,1: Fan 1 Pulses Per Rotation
+			 *      00 = 1
+			 *      01 = 2 (default)
+			 *      10 = 4
+			 *      11 = 8
+			 *   0: Shutdown Mode
+			 *      1 = Shutdown mode.
+			 *      0 = Normal operation. (default)
+			 */
+	u8 status;	/* The Status register provides all the information
+			 * about what is going on within the TC654/TC655
+			 * devices.
+			 * 7,6: Unimplemented, Read as '0'
+			 *   5: Over-Temperature Fault Condition
+			 *      1 = Over-Temperature condition has occurred
+			 *      0 = Normal operation. V IN is less than 2.6V
+			 *   4: RPM2 Counter Overflow
+			 *      1 = Fault condition
+			 *      0 = Normal operation
+			 *   3: RPM1 Counter Overflow
+			 *      1 = Fault condition
+			 *      0 = Normal operation
+			 *   2: V IN Input Status
+			 *      1 = V IN is open
+			 *      0 = Normal operation. voltage present at V IN
+			 *   1: Fan 2 Fault
+			 *      1 = Fault condition
+			 *      0 = Normal operation
+			 *   0: Fan 1 Fault
+			 *      1 = Fault condition
+			 *      0 = Normal operation
+			 */
+	u8 duty_cycle;	/* The DUTY_CYCLE register is a 4-bit read/
+			 * writable register used to control the duty
+			 * cycle of the V OUT output.
+			 */
+};
+
+/* helper to grab and cache data, at most one time per second */
+static struct tc654_data *tc654_update_client(struct device *dev)
+{
+	struct tc654_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+	int ret = 0;
+
+	mutex_lock(&data->update_lock);
+	if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) &&
+	    likely(data->valid))
+		goto out;
+
+	ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0));
+	if (ret < 0)
+		goto out;
+	data->rpm_output[0] = ret;
+
+	ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1));
+	if (ret < 0)
+		goto out;
+	data->rpm_output[1] = ret;
+
+	ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0));
+	if (ret < 0)
+		goto out;
+	data->fan_fault[0] = ret;
+
+	ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1));
+	if (ret < 0)
+		goto out;
+	data->fan_fault[1] = ret;
+
+	ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
+	if (ret < 0)
+		goto out;
+	data->config = ret;
+
+	ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS);
+	if (ret < 0)
+		goto out;
+	data->status = ret;
+
+	ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE);
+	if (ret < 0)
+		goto out;
+	data->duty_cycle = ret & 0x0f;
+
+	data->last_updated = jiffies;
+	data->valid = true;
+out:
+	mutex_unlock(&data->update_lock);
+
+	if (ret < 0)		/* upon error, encode it in return value */
+		data = ERR_PTR(ret);
+
+	return data;
+}
+
+/*
+ * sysfs attributes
+ */
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *da,
+			char *buf)
+{
+	int nr = to_sensor_dev_attr(da)->index;
+	struct tc654_data *data = tc654_update_client(dev);
+	int val;
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	if (data->config & TC654_REG_CONFIG_RES)
+		val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION;
+	else
+		val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION;
+
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
+			    char *buf)
+{
+	int nr = to_sensor_dev_attr(da)->index;
+	struct tc654_data *data = tc654_update_client(dev);
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%d\n",
+		       TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr]));
+}
+
+static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
+			   const char *buf, size_t count)
+{
+	int nr = to_sensor_dev_attr(da)->index;
+	struct tc654_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	val = clamp_val(val, 0, 12750);
+
+	mutex_lock(&data->update_lock);
+
+	data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val);
+	ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr),
+					data->fan_fault[nr]);
+
+	mutex_unlock(&data->update_lock);
+	return ret < 0 ? ret : count;
+}
+
+static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da,
+			      char *buf)
+{
+	int nr = to_sensor_dev_attr(da)->index;
+	struct tc654_data *data = tc654_update_client(dev);
+	int val;
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	if (nr == 0)
+		val = !!(data->status & TC654_REG_STATUS_F1F);
+	else
+		val = !!(data->status & TC654_REG_STATUS_F2F);
+
+	return sprintf(buf, "%d\n", val);
+}
+
+static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 };
+
+static ssize_t show_fan_pulses(struct device *dev, struct device_attribute *da,
+			      char *buf)
+{
+	int nr = to_sensor_dev_attr(da)->index;
+	struct tc654_data *data = tc654_update_client(dev);
+	u8 val;
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03);
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t set_fan_pulses(struct device *dev, struct device_attribute *da,
+			     const char *buf, size_t count)
+{
+	int nr = to_sensor_dev_attr(da)->index;
+	struct tc654_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+	u8 config;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	switch (val) {
+	case 1:
+		config = 0;
+		break;
+	case 2:
+		config = 1;
+		break;
+	case 4:
+		config = 2;
+		break;
+	case 8:
+		config = 3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(&data->update_lock);
+
+	data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]);
+	data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]);
+	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
+
+	mutex_unlock(&data->update_lock);
+	return ret < 0 ? ret : count;
+}
+
+static ssize_t show_pwm_mode(struct device *dev,
+			     struct device_attribute *da, char *buf)
+{
+	struct tc654_data *data = tc654_update_client(dev);
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%d\n", !!(data->config & TC654_REG_CONFIG_DUTYC));
+}
+
+static ssize_t set_pwm_mode(struct device *dev,
+			    struct device_attribute *da,
+			    const char *buf, size_t count)
+{
+	struct tc654_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	if (val != 0 && val != 1)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+
+	if (val)
+		data->config |= TC654_REG_CONFIG_DUTYC;
+	else
+		data->config &= ~TC654_REG_CONFIG_DUTYC;
+
+	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
+
+	mutex_unlock(&data->update_lock);
+	return ret < 0 ? ret : count;
+}
+
+static const int tc654_pwm_map[16] = { 77,  88, 102, 112, 124, 136, 148, 160,
+				      172, 184, 196, 207, 219, 231, 243, 255};
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *da,
+			       char *buf)
+{
+	struct tc654_data *data = tc654_update_client(dev);
+	int pwm;
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	if (data->config & TC654_REG_CONFIG_SDM)
+		pwm = 0;
+	else
+		pwm = tc654_pwm_map[data->duty_cycle];
+
+	return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
+			      const char *buf, size_t count)
+{
+	struct tc654_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+	if (val > 255)
+		return -EINVAL;
+
+	if (val == 0)
+		data->config |= TC654_REG_CONFIG_SDM;
+	else
+		data->config &= ~TC654_REG_CONFIG_SDM;
+
+	data->duty_cycle = find_closest(val, tc654_pwm_map,
+					ARRAY_SIZE(tc654_pwm_map));
+
+	mutex_lock(&data->update_lock);
+
+	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
+	if (ret < 0)
+		goto out;
+
+	ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE,
+				  data->duty_cycle);
+	if (ret < 0)
+		goto out;
+
+out:
+	mutex_unlock(&data->update_lock);
+	return ret < 0 ? ret : count;
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
+			  set_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
+			  set_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+			  set_fan_pulses, 0);
+static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+			  set_fan_pulses, 1);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
+			  show_pwm_mode, set_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm,
+			  set_pwm, 0);
+
+/* Driver data */
+static struct attribute *tc654_attrs[] = {
+	&sensor_dev_attr_fan1_input.dev_attr.attr,
+	&sensor_dev_attr_fan2_input.dev_attr.attr,
+	&sensor_dev_attr_fan1_min.dev_attr.attr,
+	&sensor_dev_attr_fan2_min.dev_attr.attr,
+	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
+	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
+	&sensor_dev_attr_fan1_pulses.dev_attr.attr,
+	&sensor_dev_attr_fan2_pulses.dev_attr.attr,
+	&sensor_dev_attr_pwm1_mode.dev_attr.attr,
+	&sensor_dev_attr_pwm1.dev_attr.attr,
+	NULL
+};
+
+ATTRIBUTE_GROUPS(tc654);
+
+/*
+ * device probe and removal
+ */
+
+static int tc654_probe(struct i2c_client *client,
+		       const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct tc654_data *data;
+	struct device *hwmon_dev;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -ENODEV;
+
+	data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->client = client;
+	mutex_init(&data->update_lock);
+
+	hwmon_dev =
+	    devm_hwmon_device_register_with_groups(dev, client->name, data,
+						   tc654_groups);
+	return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id tc654_id[] = {
+	{"tc654", 0},
+	{"tc655", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, tc654_id);
+
+static struct i2c_driver tc654_driver = {
+	.driver = {
+		   .name = "tc654",
+		   },
+	.probe = tc654_probe,
+	.id_table = tc654_id,
+};
+
+module_i2c_driver(tc654_driver);
+
+MODULE_AUTHOR("Allied Telesis Labs");
+MODULE_DESCRIPTION("Microchip TC654/TC655 driver");
+MODULE_LICENSE("GPL");
-- 
2.10.0.479.g7c56b16

^ permalink raw reply related

* Re: [PATCHv2] hwmon: Add tc654 driver
From: Chris Packham @ 2016-10-09 21:21 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-hwmon@vger.kernel.org, Masahiko Iwamoto, Joshua Scott,
	Kevin Tsai, Wolfram Sang, Rob Herring, Mark Rutland, Jean Delvare,
	Jonathan Corbet, linux-i2c@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-doc@vger.kernel.org
In-Reply-To: <20161007182939.GA9031@roeck-us.net>

Hi Gunter,

Thanks for the review. v3 on it's way some responses below.

On 10/08/2016 07:29 AM, Guenter Roeck wrote:
> On Fri, Oct 07, 2016 at 02:38:44PM +1300, Chris Packham wrote:
>> Add support for the tc654 and tc655 fan controllers from Microchip.
>>
>> http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
>>
>> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
>> ---
>>
>> Changes in v2:
>> - Add Documentation/hwmon/tc654
>> - Incorporate most of the review comments from Guenter. Additional error
>>   handling is added. Unused/unnecessary code is removed. I decided not
>>   to go down the regmap path yet. I may circle back to it when I look at
>>   using regmap in the adm9240 driver.
>>
>>  .../devicetree/bindings/i2c/trivial-devices.txt    |   2 +
>>  Documentation/hwmon/tc654                          |  26 ++
>>  drivers/hwmon/Kconfig                              |  11 +
>>  drivers/hwmon/Makefile                             |   1 +
>>  drivers/hwmon/tc654.c                              | 513 +++++++++++++++++++++
>>  5 files changed, 553 insertions(+)
>>  create mode 100644 Documentation/hwmon/tc654
>>  create mode 100644 drivers/hwmon/tc654.c
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
>> index 1416c6a0d2cd..833fb9f133d3 100644
>> --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
>> +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
>> @@ -122,6 +122,8 @@ microchip,mcp4662-502	Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
>>  microchip,mcp4662-103	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
>>  microchip,mcp4662-503	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
>>  microchip,mcp4662-104	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
>> +microchip,tc654		PWM Fan Speed Controller With Fan Fault Detection
>> +microchip,tc655		PWM Fan Speed Controller With Fan Fault Detection
>>  national,lm63		Temperature sensor with integrated fan control
>>  national,lm75		I2C TEMP SENSOR
>>  national,lm80		Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
>> diff --git a/Documentation/hwmon/tc654 b/Documentation/hwmon/tc654
>> new file mode 100644
>> index 000000000000..93796c5c7e79
>> --- /dev/null
>> +++ b/Documentation/hwmon/tc654
>> @@ -0,0 +1,26 @@
>> +Kernel driver tc654
>> +===================
>> +
>> +Supported chips:
>> +  * Microship TC654 and TC655
>> +    Prefix: 'tc654'
>> +    Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
>> +
>> +Authors:
>> +        Chris Packham <chris.packham@alliedtelesis.co.nz>
>> +        Masahiko Iwamoto <iwamoto@allied-telesis.co.jp>
>> +
>> +Description
>> +-----------
>> +This driver implements support for the Microchip TC654 and TC655.
>> +
>> +The TC654 used the 2-wire interface compatible with the SMBUS 2.0
>
> uses
>

Done.

>> +specification. The TC654 has two (2) inputs for measuring fan RPM and
>> +one (1) PWM output which can be used for fan control.
>> +
>> +Configuration Notes
>> +-------------------
>> +Ordinarily the pwm1_mode ABI is used for controlling the pwm output
>> +mode.  However, for this chip the output is always pwm, and the
>> +pwm1_mode determines if the pwm output is controlled via the pwm1 value
>> +or via the Vin analog input.
>
> Please describe the supported values here.
>
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 45cef3d2c75c..8681bc65cde5 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -907,6 +907,17 @@ config SENSORS_MCP3021
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called mcp3021.
>>
>> +config SENSORS_TC654
>> +	tristate "Microchip TC654/TC655 and compatibles"
>> +	depends on I2C
>> +	help
>> +	  If you say yes here you get support for TC654 and TC655.
>> +	  The TC654 and TC655 are PWM mode fan speed controllers with
>> +	  FanSense technology for use with brushless DC fans.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called tc654.
>> +
>>  config SENSORS_MENF21BMC_HWMON
>>  	tristate "MEN 14F021P00 BMC Hardware Monitoring"
>>  	depends on MFD_MENF21BMC
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index aecf4ba17460..c651f0f1d047 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_MAX6697)	+= max6697.o
>>  obj-$(CONFIG_SENSORS_MAX31790)	+= max31790.o
>>  obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>>  obj-$(CONFIG_SENSORS_MCP3021)	+= mcp3021.o
>> +obj-$(CONFIG_SENSORS_TC654)	+= tc654.o
>>  obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
>>  obj-$(CONFIG_SENSORS_NCT6683)	+= nct6683.o
>>  obj-$(CONFIG_SENSORS_NCT6775)	+= nct6775.o
>> diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c
>> new file mode 100644
>> index 000000000000..cba31cbd3383
>> --- /dev/null
>> +++ b/drivers/hwmon/tc654.c
>> @@ -0,0 +1,513 @@
>> +/*
>> + * tc654.c - Linux kernel modules for fan speed controller
>> + *
>> + * Copyright (C) 2016 Allied Telesis Labs NZ
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>
> #include <linux/bitops.h>
>
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/slab.h>
>> +#include <linux/i2c.h>
>> +#include <linux/hwmon.h>
>> +#include <linux/hwmon-sysfs.h>
>> +#include <linux/err.h>
>> +#include <linux/mutex.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/util_macros.h>
>
> Please order include files alphabetically.
>

Done.

>> +
>> +enum tc654_regs {
>> +	TC654_REG_RPM1 = 0x00,	/* RPM Output 1 */
>> +	TC654_REG_RPM2 = 0x01,	/* RPM Output 2 */
>> +	TC654_REG_FAN_FAULT1 = 0x02,	/* Fan Fault 1 Threshold */
>> +	TC654_REG_FAN_FAULT2 = 0x03,	/* Fan Fault 2 Threshold */
>> +	TC654_REG_CONFIG = 0x04,	/* Configuration */
>> +	TC654_REG_STATUS = 0x05,	/* Status */
>> +	TC654_REG_DUTY_CYCLE = 0x06,	/* Fan Speed Duty Cycle */
>> +	TC654_REG_MFR_ID = 0x07,	/* Manufacturer Identification */
>> +	TC654_REG_VER_ID = 0x08,	/* Version Identification */
>> +};
>> +
>> +/* Macros to easily index the registers */
>> +#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx))
>> +#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx))
>> +
>> +/* Config register bits */
>> +#define TC654_REG_CONFIG_RES 0x40	/* Resolution Selection */
>> +#define TC654_REG_CONFIG_DUTYC 0x20	/* Duty Cycle Control Method */
>> +#define TC654_REG_CONFIG_SDM 0x01	/* Shutdown Mode */
>> +
>> +/* Status register bits */
>> +#define TC654_REG_STATUS_F2F 0x02	/* Fan 2 Fault */
>> +#define TC654_REG_STATUS_F1F 0x01	/* Fan 1 Fault */
>> +
>
> Didn't notice earlier ... those are bits, so it would be better to use
> the BIT() macro.
>
>> +/* RPM resolution for RPM Output registers */
>> +#define TC654_HIGH_RPM_RESOLUTION 25	/* 25 RPM resolution */
>> +#define TC654_LOW_RPM_RESOLUTION 50	/* 50 RPM resolution */
>> +
>> +/* Convert to the fan fault RPM threshold from register value */
>> +#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50)	/* 50 RPM resolution */
>> +
>> +/* Convert to register value from the fan fault RPM threshold */
>> +#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff)
>> +
>> +/* Register data is read (and cached) at most once per second. */
>> +#define TC654_UPDATE_INTERVAL	HZ
>> +
>> +struct tc654_data {
>> +	struct i2c_client *client;
>> +	struct device *hwmon_dev;
>
> No longer needed. Just keep the variable local in the probe function.
>
>> +
>> +	/* update mutex */
>> +	struct mutex update_lock;
>> +
>> +	/* tc654 register cache */
>> +	bool valid;
>> +	unsigned long last_updated;	/* in jiffies */
>> +
>> +	u8 rpm_output[2];	/* The fan RPM data for fans 1 and 2 is then
>> +				 * written to registers RPM1 and RPM2
>> +				 */
>> +	u8 fan_fault[2];	/* The Fan Fault Threshold Registers are used to
>> +				 * set the fan fault threshold levels for fan 1
>> +				 * and fan 2
>> +				 */
>> +	u8 config;	/* The Configuration Register is an 8-bit read/
>> +			 * writable multi-function control register
>> +			 *   7: Fan Fault Clear
>> +			 *      1 = Clear Fan Fault
>> +			 *      0 = Normal Operation (default)
>> +			 *   6: Resolution Selection for RPM Output Registers
>> +			 *      RPM Output Registers (RPM1 and RPM2) will be
>> +			 *      set for
>> +			 *      1 = 25 RPM (9-bit) resolution
>> +			 *      0 = 50 RPM (8-bit) resolution (default)
>> +			 *   5: Duty Cycle Control Method
>> +			 *      The V OUT duty cycle will be controlled via
>> +			 *      1 = the SMBus interface.
>> +			 *      0 = via the V IN analog input pin. (default)
>> +			 * 4,3: Fan 2 Pulses Per Rotation
>> +			 *      00 = 1
>> +			 *      01 = 2 (default)
>> +			 *      10 = 4
>> +			 *      11 = 8
>> +			 * 2,1: Fan 1 Pulses Per Rotation
>> +			 *      00 = 1
>> +			 *      01 = 2 (default)
>> +			 *      10 = 4
>> +			 *      11 = 8
>> +			 *   0: Shutdown Mode
>> +			 *      1 = Shutdown mode.
>> +			 *      0 = Normal operation. (default)
>> +			 */
>> +	u8 status;	/* The Status register provides all the information
>> +			 * about what is going on within the TC654/TC655
>> +			 * devices.
>> +			 * 7,6: Unimplemented, Read as '0'
>> +			 *   5: Over-Temperature Fault Condition
>> +			 *      1 = Over-Temperature condition has occurred
>> +			 *      0 = Normal operation. V IN is less than 2.6V
>> +			 *   4: RPM2 Counter Overflow
>> +			 *      1 = Fault condition
>> +			 *      0 = Normal operation
>> +			 *   3: RPM1 Counter Overflow
>> +			 *      1 = Fault condition
>> +			 *      0 = Normal operation
>> +			 *   2: V IN Input Status
>> +			 *      1 = V IN is open
>> +			 *      0 = Normal operation. voltage present at V IN
>> +			 *   1: Fan 2 Fault
>> +			 *      1 = Fault condition
>> +			 *      0 = Normal operation
>> +			 *   0: Fan 1 Fault
>> +			 *      1 = Fault condition
>> +			 *      0 = Normal operation
>> +			 */
>> +	u8 duty_cycle;	/* The DUTY_CYCLE register is a 4-bit read/
>> +			 * writable register used to control the duty
>> +			 * cycle of the V OUT output.
>> +			 */
>> +};
>> +
>> +/* helper to grab and cache data, at most one time per second */
>> +static struct tc654_data *tc654_update_client(struct device *dev)
>> +{
>> +	struct tc654_data *data = dev_get_drvdata(dev);
>> +	struct i2c_client *client = data->client;
>> +	int ret = 0;
>> +
>> +	mutex_lock(&data->update_lock);
>> +	if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) &&
>> +	    likely(data->valid))
>> +		goto out;
>> +
>> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0));
>> +	if (ret < 0)
>> +		goto out;
>> +	data->rpm_output[0] = ret;
>> +
>> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1));
>> +	if (ret < 0)
>> +		goto out;
>> +	data->rpm_output[1] = ret;
>> +
>> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0));
>> +	if (ret < 0)
>> +		goto out;
>> +	data->fan_fault[0] = ret;
>> +
>> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1));
>> +	if (ret < 0)
>> +		goto out;
>> +	data->fan_fault[1] = ret;
>> +
>> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
>> +	if (ret < 0)
>> +		goto out;
>> +	data->config = ret;
>> +
>> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS);
>> +	if (ret < 0)
>> +		goto out;
>> +	data->status = ret;
>> +
>> +	ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE);
>> +	if (ret < 0)
>> +		goto out;
>> +	data->duty_cycle = ret;
>
> Maybe make it
> 	data->duty_cycle = ret & 0x0f;
>
> While that should not be necessary, it doesn't hurt, and the datasheet isn't
> entirely clear if the upper bits are guaranteed to be 0.
>

Done.

>> +
>> +	data->last_updated = jiffies;
>> +	data->valid = true;
>> +out:
>> +	mutex_unlock(&data->update_lock);
>> +
>> +	if (ret < 0)		/* upon error, encode it in return value */
>> +		data = ERR_PTR(ret);
>> +
>> +	return data;
>> +}
>> +
>> +/*
>> + * sysfs attributes
>> + */
>> +
>> +static ssize_t show_fan(struct device *dev, struct device_attribute *da,
>> +			char *buf)
>> +{
>> +	int nr = to_sensor_dev_attr(da)->index;
>> +	struct tc654_data *data = tc654_update_client(dev);
>> +	int val;
>> +
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +
>> +	if (data->config & TC654_REG_CONFIG_RES)
>> +		val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION;
>> +	else
>> +		val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION;
>> +
>> +	return sprintf(buf, "%d\n", val);
>> +}
>> +
>> +static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
>> +			    char *buf)
>> +{
>> +	int nr = to_sensor_dev_attr(da)->index;
>> +	struct tc654_data *data = tc654_update_client(dev);
>> +
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +
>> +	return sprintf(buf, "%d\n",
>> +		       TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr]));
>> +}
>> +
>> +static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
>> +			   const char *buf, size_t count)
>> +{
>> +	int nr = to_sensor_dev_attr(da)->index;
>> +	struct tc654_data *data = dev_get_drvdata(dev);
>> +	struct i2c_client *client = data->client;
>> +	unsigned long val;
>> +	int ret;
>> +
>> +	if (kstrtoul(buf, 10, &val))
>> +		return -EINVAL;
>> +
>> +	clamp_val(val, 0, 12750);
>
> 	val = clamp_val(val, 0, 12750);
>> +
>> +	mutex_lock(&data->update_lock);
>> +
>> +	data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val);
>> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr),
>> +				  data->fan_fault[nr]);
>
> Hmmm ... the reason for asking you to align continuation lines with '('
> is that it makes the code more uniform and helps me review it. I understand
> that people sometimes don't like it, but please keep in mind that it helps
> with the code review.
>

Sorry about that. Didn't re-align when I added the 'ret ='.

>> +
>> +	mutex_unlock(&data->update_lock);
>> +	return ret < 0 ? ret : count;
>> +}
>> +
>> +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da,
>> +			      char *buf)
>> +{
>> +	int nr = to_sensor_dev_attr(da)->index;
>> +	struct tc654_data *data = tc654_update_client(dev);
>> +	int val;
>> +
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +
>> +	if (nr == 0)
>> +		val = !!(data->status & TC654_REG_STATUS_F1F);
>> +	else
>> +		val = !!(data->status & TC654_REG_STATUS_F2F);
>> +
>> +	return sprintf(buf, "%d\n", val);
>> +}
>> +
>> +static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 };
>> +
>> +static ssize_t show_fan_pulses(struct device *dev, struct device_attribute *da,
>> +			      char *buf)
>> +{
>> +	int nr = to_sensor_dev_attr(da)->index;
>> +	struct tc654_data *data = tc654_update_client(dev);
>> +	u8 val;
>> +
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +
>> +	val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03);
>> +	return sprintf(buf, "%d\n", val);
>> +}
>> +
>> +static ssize_t set_fan_pulses(struct device *dev, struct device_attribute *da,
>> +			     const char *buf, size_t count)
>> +{
>> +	int nr = to_sensor_dev_attr(da)->index;
>> +	struct tc654_data *data = dev_get_drvdata(dev);
>> +	struct i2c_client *client = data->client;
>> +	u8 config;
>> +	unsigned long val;
>> +	int ret;
>> +
>> +	if (kstrtoul(buf, 10, &val))
>> +		return -EINVAL;
>> +
>> +	switch (val) {
>> +	case 1:
>> +		config = 0;
>> +		break;
>> +	case 2:
>> +		config = 1;
>> +		break;
>> +	case 4:
>> +		config = 2;
>> +		break;
>> +	case 8:
>> +		config = 3;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&data->update_lock);
>> +
>> +	data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]);
>> +	data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]);
>> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
>> +
>> +	mutex_unlock(&data->update_lock);
>> +	return ret < 0 ? ret : count;
>> +}
>> +
>> +static ssize_t show_pwm_mode(struct device *dev,
>> +			     struct device_attribute *da, char *buf)
>> +{
>> +	struct tc654_data *data = tc654_update_client(dev);
>> +
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +
>> +	return sprintf(buf, "%d\n", data->config & TC654_REG_CONFIG_DUTYC);
>
> Should be
> 				!!(data->config & TC654_REG_CONFIG_DUTYC)
> otherwise it displays 0 or 32.
>

Done.

>> +}
>> +
>> +static ssize_t set_pwm_mode(struct device *dev,
>> +			    struct device_attribute *da,
>> +			    const char *buf, size_t count)
>> +{
>> +	struct tc654_data *data = dev_get_drvdata(dev);
>> +	struct i2c_client *client = data->client;
>> +	unsigned long val;
>> +	int ret;
>> +
>> +	if (kstrtoul(buf, 10, &val))
>> +		return -EINVAL;
>> +
>> +	if (val != 0 && val != 1)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&data->update_lock);
>> +
>> +	if (val)
>> +		data->config |= TC654_REG_CONFIG_DUTYC;
>> +	else
>> +		data->config &= ~TC654_REG_CONFIG_DUTYC;
>> +
>> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
>> +
>> +	mutex_unlock(&data->update_lock);
>> +	return ret < 0 ? ret : count;
>> +}
>> +
>> +static const int tc654_pwm_map[16] = { 76, 88, 100, 112, 124, 141, 147, 171,
>> +		183, 195, 207, 219, 231, 243, 255 };
>> +
>
> This lists 15 entries for an array of size 16, leaving the last entry
> at 0. Is there an entry missing ?
>
> Also, 141 yields 55.29%, which doesn't match the datasheet.
>
> I ended up spending some time to match the numbers:
>
> map	%	datasheet
> 76	29.8	30.0
> 88	34.5	34.67
> 100	39.21	39.33
> 112	43.92	44.0
> 124	48.62	48.67
> 141	55.29	53.33	off (136 would be 53.33%)
> 147	57.64	58.0	148 would be 58.03%
> ??	??	62.67	missing (160 would be 62.67%)
> 171	67.05	67.33	172 would be 67.45%
> 183	71.76	72.0	184 would be 72.15%
> 195	76.47	76.67
> 207	81.17	81.33
> 219	85.88	86.0
> 231	90.58	90.67
> 243	95.29	95.33
> 255	100	100
>

Thanks for (re-)doing my math. I initially did these by hand so I'm not 
surprised I missed one. I've put all the values through a spreadsheet 
and used rounding to come up with the following

map	%	datasheet
77	30.20%	30.00%
88	34.51%	34.67%
102	40.00%	39.93%
112	43.92%	44.00%
124	48.63%	48.67%
136	53.33%	53.33%
148	58.04%	58.00%
160	62.75%	62.67%
172	67.45%	67.33%
184	72.16%	72.00%
196	76.86%	76.67%
207	81.18%	81.33%
219	85.88%	86.00%
231	90.59%	90.67%
243	95.29%	95.33%
255	100.00%	100.00%

Differences are due to rounding.

>> +static ssize_t show_pwm(struct device *dev, struct device_attribute *da,
>> +			       char *buf)
>> +{
>> +	struct tc654_data *data = tc654_update_client(dev);
>> +	int pwm;
>> +
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +
>> +	if (data->config & TC654_REG_CONFIG_SDM)
>> +		pwm = 0;
>> +	else
>> +		pwm = tc654_pwm_map[data->duty_cycle];
>> +
>> +	return sprintf(buf, "%d\n", pwm);
>> +}
>> +
>> +static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
>> +			      const char *buf, size_t count)
>> +{
>> +	struct tc654_data *data = dev_get_drvdata(dev);
>> +	struct i2c_client *client = data->client;
>> +	unsigned long val;
>> +	int ret;
>> +
>> +	if (kstrtoul(buf, 10, &val))
>> +		return -EINVAL;
>> +	if (val > 255)
>> +		return -EINVAL;
>> +
>> +	if (val == 0)
>> +		data->config |= TC654_REG_CONFIG_SDM;
>> +	else
>> +		data->config &= ~TC654_REG_CONFIG_SDM;
>> +
>> +	data->duty_cycle = find_closest(val, tc654_pwm_map,
>> +					ARRAY_SIZE(tc654_pwm_map));
>> +
>> +	mutex_lock(&data->update_lock);
>> +
>> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
>> +	if (ret < 0)
>> +		goto out;
>> +
>> +	ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE,
>> +				  data->duty_cycle);
>> +	if (ret < 0)
>> +		goto out;
>> +
>> +out:
>> +	mutex_unlock(&data->update_lock);
>> +	return ret < 0 ? ret : count;
>> +}
>> +
>> +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
>> +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
>> +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
>> +			  set_fan_min, 0);
>> +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
>> +			  set_fan_min, 1);
>> +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
>> +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
>> +static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
>> +			  set_fan_pulses, 0);
>> +static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
>> +			  set_fan_pulses, 1);
>> +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
>> +			  show_pwm_mode, set_pwm_mode, 0);
>> +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm,
>> +			  set_pwm, 0);
>> +
>> +/* Driver data */
>> +static struct attribute *tc654_attrs[] = {
>> +	&sensor_dev_attr_fan1_input.dev_attr.attr,
>> +	&sensor_dev_attr_fan2_input.dev_attr.attr,
>> +	&sensor_dev_attr_fan1_min.dev_attr.attr,
>> +	&sensor_dev_attr_fan2_min.dev_attr.attr,
>> +	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
>> +	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
>> +	&sensor_dev_attr_fan1_pulses.dev_attr.attr,
>> +	&sensor_dev_attr_fan2_pulses.dev_attr.attr,
>> +	&sensor_dev_attr_pwm1_mode.dev_attr.attr,
>> +	&sensor_dev_attr_pwm1.dev_attr.attr,
>> +	NULL
>> +};
>> +
>> +ATTRIBUTE_GROUPS(tc654);
>> +
>> +/*
>> + * device probe and removal
>> + */
>> +
>> +static int tc654_probe(struct i2c_client *client,
>> +		       const struct i2c_device_id *id)
>> +{
>> +	struct device *dev = &client->dev;
>> +	struct tc654_data *data;
>> +
>> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
>> +		return -ENODEV;
>> +
>> +	data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL);
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	data->client = client;
>> +	i2c_set_clientdata(client, data);
>
> No longer needed.

Done.

>
>> +	mutex_init(&data->update_lock);
>> +
>> +	data->hwmon_dev =
>> +	    devm_hwmon_device_register_with_groups(dev, client->name, data,
>> +						   tc654_groups);
>> +	if (IS_ERR(data->hwmon_dev))
>> +		return PTR_ERR(data->hwmon_dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct i2c_device_id tc654_id[] = {
>> +	{"tc654", 0},
>> +	{"tc655", 0},
>> +	{}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(i2c, tc654_id);
>> +
>> +static struct i2c_driver tc654_driver = {
>> +	.driver = {
>> +		   .name = "tc654",
>> +		   .owner = THIS_MODULE,
>
> Not needed (see Julia's patch)
>

Will incorporate those changes too.

>> +		   },
>> +	.probe = tc654_probe,
>> +	.id_table = tc654_id,
>> +};
>> +
>> +module_i2c_driver(tc654_driver);
>> +
>> +MODULE_AUTHOR("Allied Telesis Labs");
>> +MODULE_DESCRIPTION("Microchip TC654/TC655 driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.10.0.479.g7c56b16
>>
>


^ permalink raw reply

* Re: [PATCH v2 1/2] Documentation: tpm: add the IBM Virtual TPM device tree binding documentation
From: Rob Herring @ 2016-10-08 21:11 UTC (permalink / raw)
  To: Nayna Jain
  Cc: mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	pawel.moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	wsa-z923LK4zBo2bacvFa/9K2g,
	honclo-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	tpmdd-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	cclaudio-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
In-Reply-To: <1475051441-23008-1-git-send-email-nayna-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>

On Wed, Sep 28, 2016 at 04:30:40AM -0400, Nayna Jain wrote:
> Virtual TPM, which is being used on IBM POWER7+ and POWER8 systems running
> POWERVM, is currently supported by tpm device driver but lacks the
> documentation. This patch adds the missing documentation for the existing
> support.
> 
> Suggested-by: Jason Gunthorpe <jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/@public.gmane.org>
> Signed-off-by: Nayna Jain <nayna-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
> ---
> Changelog v2:
> 
> - New Patch
> 
>  .../devicetree/bindings/security/tpm/ibmvtpm.txt   | 41 ++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
> 
> diff --git a/Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt b/Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
> new file mode 100644
> index 0000000..d89f999
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
> @@ -0,0 +1,41 @@
> +* Device Tree Bindings for IBM Virtual Trusted Platform Module(vtpm)
> +
> +Required properties:
> +
> +- compatible            : property name that conveys the platform architecture
> +                          identifiers, as 'IBM,vtpm'

You use IBM here, but...

> +- device_type           : specifies type of virtual device
> +- interrupts            : property specifying the interrupt source number and
> +                          sense code associated with this virtual I/O Adapters
> +- ibm,my-drc-index      : integer index for the connector between the device

use ibm here. These should be the same unless there's some history I'm 
not aware of.

> +                          and its parent - present only if Dynamic
> +                          Reconfiguration(DR) Connector is enabled
> +- ibm,#dma-address-cells: specifies the number of cells that are used to
> +                          encode the physical address field of dma-window
> +                          properties
> +- ibm,#dma-size-cells   : specifies the number of cells that are used to
> +                          encode the size field of dma-window properties
> +- ibm,my-dma-window     : specifies DMA window associated with this virtual
> +                          IOA

Are these "standard" IBM properties? Does dma-ranges not work for you?

> +- ibm,loc-code          : specifies the unique and persistent location code
> +                          associated with this virtual I/O Adapters
> +- linux,sml-base        : 64-bit base address of the reserved memory allocated
> +                          for the firmware event log
> +- linux,sml-size        : size of the memory allocated for the firmware event log
> +
> +Example (IBM Virtual Trusted Platform Module)
> +---------------------------------------------
> +
> +                vtpm@30000003 {
> +                        ibm,#dma-size-cells = <0x2>;
> +                        compatible = "IBM,vtpm";
> +                        device_type = "IBM,vtpm";
> +                        ibm,my-drc-index = <0x30000003>;
> +                        ibm,#dma-address-cells = <0x2>;
> +                        linux,sml-base = <0xc60e 0x0>;
> +                        interrupts = <0xa0003 0x0>;
> +                        ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;
> +                        ibm,loc-code = "U8286.41A.10082DV-V3-C3";
> +                        reg = <0x30000003>;
> +                        linux,sml-size = <0xbce10200>;
> +                };
> -- 
> 2.5.0
> 

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot

^ permalink raw reply

* Re: [PATCH v2 2/2] Documentation: tpm: add the Physical TPM device tree binding documentation
From: Rob Herring @ 2016-10-08 21:05 UTC (permalink / raw)
  To: Nayna Jain
  Cc: devicetree, tpmdd-devel, wsa, pawel.moll, mark.rutland,
	ijc+devicetree, galak, linux-i2c, peterhuewe, tpmdd,
	jarkko.sakkinen, hellerda, ltcgcw, cclaudio, honclo
In-Reply-To: <1475051441-23008-2-git-send-email-nayna@linux.vnet.ibm.com>

On Wed, Sep 28, 2016 at 04:30:41AM -0400, Nayna Jain wrote:
> Newly added support of TPM 2.0 eventlog securityfs pseudo files in tpm
> device driver consumes device tree bindings representing I2C based
> Physical TPM. This patch adds the documentation for corresponding device
> tree bindings of I2C based Physical TPM. These bindings are similar to
> vtpm device tree bindings being used on IBM Power7+ and Power8 Systems
> running PowerVM.
> 
> Suggested-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
> Signed-off-by: Nayna Jain <nayna@linux.vnet.ibm.com>
> ---
> Changelog v2:
> 
> - Include review feedbacks.
>   - Move the doc within bindings/security/tpm.
>   - Add example for compatible property in description.
>   - Delete implicit properties like status, label from description.
>   - Redefine linux,sml-base description.
> 
>  .../devicetree/bindings/security/tpm/tpm-i2c.txt     | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/security/tpm/tpm-i2c.txt
> 
> diff --git a/Documentation/devicetree/bindings/security/tpm/tpm-i2c.txt b/Documentation/devicetree/bindings/security/tpm/tpm-i2c.txt
> new file mode 100644
> index 0000000..16df8bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/security/tpm/tpm-i2c.txt
> @@ -0,0 +1,20 @@
> +* Device Tree Bindings for I2C based Trusted Platform Module(TPM)
> +
> +Required properties:
> +
> +- compatible     : 'manufacturer,model', eg. nuvoton,npct650
> +- linux,sml-base : 64-bit base address of the reserved memory allocated for
> +                   the firmware event log
> +- linux,sml-size : size of the memory allocated for the firmware event log
> +
> +Example (for OpenPower Systems with Nuvoton TPM 2.0 on I2C)
> +----------------------------------------------------------
> +
> +tpm@57 {
> +	reg = <0x57>;
> +	label = "tpm";

Not documented, but why? label is really only useful when you have 
multiple items that humans need to identify like ports on ethernet 
switch or LEDs.

> +	compatible = "nuvoton,npct650", "nuvoton,npct601";
> +	linux,sml-base = <0x7f 0xfd450000>;
> +	linux,sml-size = <0x10000>;
> +	status = "okay";
> +};
> -- 
> 2.5.0
> 

^ permalink raw reply

* Re: [PATCH v2 1/2] Documentation: tpm: add the IBM Virtual TPM device tree binding documentation
From: Rob Herring @ 2016-10-08 21:01 UTC (permalink / raw)
  To: Nayna
  Cc: mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	pawel.moll-5wv7dgnIgG8, wsa-z923LK4zBo2bacvFa/9K2g,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	honclo-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	tpmdd-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	cclaudio-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
In-Reply-To: <57F55F28.1090005-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>

On Thu, Oct 06, 2016 at 01:44:32AM +0530, Nayna wrote:
> 
> 
> On 09/29/2016 04:34 PM, Jarkko Sakkinen wrote:
> >On Wed, Sep 28, 2016 at 04:30:40AM -0400, Nayna Jain wrote:
> >>Virtual TPM, which is being used on IBM POWER7+ and POWER8 systems running
> >>POWERVM, is currently supported by tpm device driver but lacks the
> >>documentation. This patch adds the missing documentation for the existing
> >>support.
> >>
> >>Suggested-by: Jason Gunthorpe <jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/@public.gmane.org>
> >>Signed-off-by: Nayna Jain <nayna-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
> >>---
> >>Changelog v2:
> >>
> >>- New Patch
> >>
> >>  .../devicetree/bindings/security/tpm/ibmvtpm.txt   | 41 ++++++++++++++++++++++
> >>  1 file changed, 41 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
> >>
> >>diff --git a/Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt b/Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
> >>new file mode 100644
> >>index 0000000..d89f999
> >>--- /dev/null
> >>+++ b/Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
> >>@@ -0,0 +1,41 @@
> >>+* Device Tree Bindings for IBM Virtual Trusted Platform Module(vtpm)
> >>+
> >>+Required properties:
> >>+
> >>+- compatible            : property name that conveys the platform architecture
> >>+                          identifiers, as 'IBM,vtpm'
> >>+- device_type           : specifies type of virtual device
> >
> >A generic device tree question. What is the difference between
> >these fields? Why the I2C one does have 'device_type'?
> 
> Please find the details as below:
> 
> compatible - Standard property name as per IEEE 1275, specifying the
> interface compatible with this device. This property is consumed by linux
> kernel for selection of device driver.
> 
> device_type - Standard property name as per IEEE 1275, specifying the device
> type. This property MAY be used by linux kernel for device driver selection.
> It is used in the case of IBM virtual TPM driver.

AIUI, this should be a standard value such as serial, pci, etc. I don't 
think your use here is correct, but I could be wrong. I'm not certain 
what you do with devices that don't have a standard type.

> 
> /**
>  * vio_register_device_node: - Register a new vio device.
>  * @of_node:    The OF node for this device.
>  *
>  * Creates and initializes a vio_dev structure from the data in
>  * of_node and adds it to the list of virtual devices.
>  * Returns a pointer to the created vio_dev or NULL if node has
>  * NULL device_type or compatible fields.
>  */
> struct vio_dev *vio_register_device_node(struct device_node *of_node)
> 
> and vtpm device table being defined as below:
> 
> static struct vio_device_id tpm_ibmvtpm_device_table[] = {
>         { "IBM,vtpm", "IBM,vtpm"}, ----------------------------------->
> type,compat
>         { "", "" }
> };
> 
> So, vio (virtual) devices uses both device_type and compatible property for
> device registration and driver selection.
> In case of physical TPM, it is only compatible property being used for
> device driver selection
> 
> Also, please note that device_type property is now deprecated in latest
> Device Tree specs.

Deprecated for Flattened DT only. OpenFirmware implementations 
can/should still use this.

Rob

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot

^ permalink raw reply

* Re: [PULL REQUEST] i2c for 4.9
From: Wolfram Sang @ 2016-10-08 19:42 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-i2c, Linux Kernel Mailing List
In-Reply-To: <CA+55aFxDBznCn_ui65W2F06ubZkdVVt2nQWn+ACFQpAQ26+aUw@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 457 bytes --]

On Fri, Oct 07, 2016 at 02:17:20PM -0700, Linus Torvalds wrote:
> On Fri, Oct 7, 2016 at 2:32 AM, Wolfram Sang <wsa@the-dreams.de> wrote:
> >
> > here is the 4.9 pull request from I2C including:
> 
> Would you mind double-checking my merge.
> 
> It looked very trivial, but it would be good to have somebody else
> give that gpio-pca953x.c conflict a look to verify that I didn't screw
> up the 'id' variable split.

Looks good to me. Thanks!


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* RE: [v12, 0/8] Fix eSDHC host version register bug
From: Y.B. Lu @ 2016-10-08  3:28 UTC (permalink / raw)
  To: Y.B. Lu, linux-mmc@vger.kernel.org, ulf.hansson@linaro.org,
	Scott Wood, Arnd Bergmann
  Cc: linuxppc-dev@lists.ozlabs.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org,
	linux-i2c@vger.kernel.org, iommu@lists.linux-foundation.org,
	netdev@vger.kernel.org, Mark Rutland, Rob Herring, Russell King,
	Jochen Friedrich, Joerg Roedel, Claudiu Manoil, Bhupesh Sharma
In-Reply-To: <1474441040-11946-1-git-send-email-yangbo.lu@nxp.com>

Hi Uffe, Arnd and Scott,

Any comments on this latest patcheset?
Could we consider to merge it if no any other changes needed?
:)


Thanks.

Best regards,
Yangbo Lu

> -----Original Message-----
> From: Y.B. Lu
> Sent: Monday, September 26, 2016 11:15 AM
> To: linux-mmc@vger.kernel.org; ulf.hansson@linaro.org; Scott Wood; Arnd
> Bergmann
> Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; linux-
> clk@vger.kernel.org; linux-i2c@vger.kernel.org; iommu@lists.linux-
> foundation.org; netdev@vger.kernel.org; Mark Rutland; Rob Herring;
> Russell King; Jochen Friedrich; Joerg Roedel; Claudiu Manoil; Bhupesh
> Sharma; Qiang Zhao; Kumar Gala; Santosh Shilimkar; Leo Li; X.B. Xie; M.H.
> Lian
> Subject: RE: [v12, 0/8] Fix eSDHC host version register bug
> 
> Any comments about this version patchset ?
> 
> :)
> 
> 
> > -----Original Message-----
> > From: Yangbo Lu [mailto:yangbo.lu@nxp.com]
> > Sent: Wednesday, September 21, 2016 2:57 PM
> > To: linux-mmc@vger.kernel.org; ulf.hansson@linaro.org; Scott Wood;
> > Arnd Bergmann
> > Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org;
> > linux-arm- kernel@lists.infradead.org; linux-kernel@vger.kernel.org;
> > linux- clk@vger.kernel.org; linux-i2c@vger.kernel.org;
> > iommu@lists.linux- foundation.org; netdev@vger.kernel.org; Mark
> > Rutland; Rob Herring; Russell King; Jochen Friedrich; Joerg Roedel;
> > Claudiu Manoil; Bhupesh Sharma; Qiang Zhao; Kumar Gala; Santosh
> Shilimkar; Leo Li; X.B. Xie; M.H.
> > Lian; Y.B. Lu
> > Subject: [v12, 0/8] Fix eSDHC host version register bug
> >
> > This patchset is used to fix a host version register bug in the T4240-
> > R1.0-R2.0 eSDHC controller. To match the SoC version and revision, 10
> > previous version patchsets had tried many methods but all of them were
> > rejected by reviewers.
> > Such as
> > 	- dts compatible method
> > 	- syscon method
> > 	- ifdef PPC method
> > 	- GUTS driver getting SVR method
> > Anrd suggested a soc_device_match method in v10, and this is the only
> > available method left now. This v11 patchset introduces the
> > soc_device_match interface in soc driver.
> >
> > The first six patches of Yangbo are to add the GUTS driver. This is
> > used to register a soc device which contain soc version and revision
> > information.
> > The other two patches introduce the soc_device_match method in soc
> > driver and apply it on esdhc driver to fix this bug.
> >
> > Arnd Bergmann (1):
> >   base: soc: introduce soc_device_match() interface
> >
> > Yangbo Lu (7):
> >   dt: bindings: update Freescale DCFG compatible
> >   ARM64: dts: ls2080a: add device configuration node
> >   dt: bindings: move guts devicetree doc out of powerpc directory
> >   powerpc/fsl: move mpc85xx.h to include/linux/fsl
> >   soc: fsl: add GUTS driver for QorIQ platforms
> >   MAINTAINERS: add entry for Freescale SoC drivers
> >   mmc: sdhci-of-esdhc: fix host version for T4240-R1.0-R2.0
> >
> >  Documentation/devicetree/bindings/arm/fsl.txt      |   6 +-
> >  .../bindings/{powerpc => soc}/fsl/guts.txt         |   3 +
> >  MAINTAINERS                                        |  11 +-
> >  arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi     |   6 +
> >  arch/powerpc/kernel/cpu_setup_fsl_booke.S          |   2 +-
> >  arch/powerpc/sysdev/fsl_pci.c                      |   2 +-
> >  drivers/base/Kconfig                               |   1 +
> >  drivers/base/soc.c                                 |  66 ++++++
> >  drivers/clk/clk-qoriq.c                            |   3 +-
> >  drivers/i2c/busses/i2c-mpc.c                       |   2 +-
> >  drivers/iommu/fsl_pamu.c                           |   3 +-
> >  drivers/mmc/host/Kconfig                           |   1 +
> >  drivers/mmc/host/sdhci-of-esdhc.c                  |  20 ++
> >  drivers/net/ethernet/freescale/gianfar.c           |   2 +-
> >  drivers/soc/Kconfig                                |   2 +-
> >  drivers/soc/fsl/Kconfig                            |  19 ++
> >  drivers/soc/fsl/Makefile                           |   1 +
> >  drivers/soc/fsl/guts.c                             | 257
> > +++++++++++++++++++++
> >  include/linux/fsl/guts.h                           | 125 ++++++----
> >  .../asm/mpc85xx.h => include/linux/fsl/svr.h       |   4 +-
> >  include/linux/sys_soc.h                            |   3 +
> >  21 files changed, 478 insertions(+), 61 deletions(-)  rename
> > Documentation/devicetree/bindings/{powerpc => soc}/fsl/guts.txt (91%)
> > create mode 100644 drivers/soc/fsl/Kconfig  create mode 100644
> > drivers/soc/fsl/guts.c  rename arch/powerpc/include/asm/mpc85xx.h =>
> > include/linux/fsl/svr.h (97%)
> >
> > --
> > 2.1.0.27.g96db324


^ permalink raw reply

* Re: [PATCH 00/10] Introduce Juniper PTXPMB CPLD driver
From: Frank Rowand @ 2016-10-08  2:17 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Rob Herring, Linus Walleij, Alexandre Courbot, Mark Rutland,
	Wolfram Sang, David Woodhouse, Brian Norris, Wim Van Sebroeck,
	Guenter Roeck, Peter Rosin, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Rajat Jain,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <57F8324C.2040804-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On 10/07/16 16:39, Frank Rowand wrote:
> On 10/07/16 08:17, Pantelis Antoniou wrote:
>> Add Juniper's PTXPMB FPGA CPLD driver. Those FPGAs
>> are present in Juniper's PTX series of routers.
>>
>> The MFD driver provices watchdog/i2c/gpio/mtd devices.
>>
>> There are full device tree binding documents for the
>> master mfd driver and for all slave drivers.
>>
>> This patchset is against mainline as of today: v4.8-9431-g3477d16
>> and is dependent on the "Juniper prerequisites" and
>> "Juniper infrastructure" patchsets sent earlier.
> 
> Pointers to the patchsets please.
> 

Nevermind.  Found them.

-Frank

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 00/10] Introduce Juniper PTXPMB CPLD driver
From: Frank Rowand @ 2016-10-07 23:39 UTC (permalink / raw)
  To: Pantelis Antoniou, Lee Jones
  Cc: Rob Herring, Linus Walleij, Alexandre Courbot, Mark Rutland,
	Wolfram Sang, David Woodhouse, Brian Norris, Wim Van Sebroeck,
	Guenter Roeck, Peter Rosin, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Rajat Jain,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1475853451-22121-1-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>

On 10/07/16 08:17, Pantelis Antoniou wrote:
> Add Juniper's PTXPMB FPGA CPLD driver. Those FPGAs
> are present in Juniper's PTX series of routers.
> 
> The MFD driver provices watchdog/i2c/gpio/mtd devices.
> 
> There are full device tree binding documents for the
> master mfd driver and for all slave drivers.
> 
> This patchset is against mainline as of today: v4.8-9431-g3477d16
> and is dependent on the "Juniper prerequisites" and
> "Juniper infrastructure" patchsets sent earlier.

Pointers to the patchsets please.

< snip >

-Frank

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PULL REQUEST] i2c for 4.9
From: Linus Torvalds @ 2016-10-07 21:17 UTC (permalink / raw)
  To: Wolfram Sang; +Cc: linux-i2c, Linux Kernel Mailing List
In-Reply-To: <20161007093226.GA2058@katana>

On Fri, Oct 7, 2016 at 2:32 AM, Wolfram Sang <wsa@the-dreams.de> wrote:
>
> here is the 4.9 pull request from I2C including:

Would you mind double-checking my merge.

It looked very trivial, but it would be good to have somebody else
give that gpio-pca953x.c conflict a look to verify that I didn't screw
up the 'id' variable split.

                    Linus

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox