Re: [PATCH linux] drivers/fsi: Add SBEFIFO FSI client device driver

2017-06-20 Thread Eddie James



On 06/15/2017 5:24 AM, gre...@linuxfoundation.org 
 wrote:

On Wed, Jun 14, 2017 at 02:47:27PM -0500, Edward A. James wrote:

+struct sbefifo {
+struct timer_list poll_timer;
+struct fsi_device *fsi_dev;
+struct miscdevice mdev;
+wait_queue_head_t wait;
+struct list_head link;
+struct list_head xfrs;
+struct kref kref;
+spinlock_t lock;
+char name[32];
+int idx;
+int rc;
+};


You have a misc device, a pointer to a fsi_device, and a kref in this
structure.  Which one actually does the reference counting?  It seems
there are 3 different ways it could happen.  That's not right at all,
and ripe for lots and lots of confusion.  Only use one please.


Thanks for the comments, the problem here is that there is no way to 
init the miscdevice device kobject with a kobj_type. So we'd have to 
just do raw kref get/put there so we can get our release function. Does 
any other linux driver use miscdevice for reference counting? I couldn't 
find anything. It seems cleaner to me to have our own kref. The fsi_dev 
is really a parent device, so we shouldn't use that for reference counting.


What do you think? I can add comments to make things clearer?

Thanks,
Eddie



thanks,

greg k-h





[PATCH linux 0/4] drivers/fsi: Add SBEFIFO and OCC drivers

2017-06-21 Thread Eddie James
From: "Edward A. James" 

This series adds two FSI-based device drivers. The OCC driver is dependent on
the SBEFIFO driver, as a user of it's in-kernel API. The in-kernel API provided
by the OCC driver will be used by a hwmon driver (to be sent to the lkml soon).

I previously sent up the first patch in the series independently, but decided
to split into the base driver and the addition of the in-kernel API, as it's
a little easier to digest. So, no v2, but I did include some fixes suggested by
Greg.

Edward A. James (4):
  drivers/fsi: Add SBEFIFO FSI client device driver
  drivers/fsi/sbefifo: Add in-kernel API
  drivers/fsi: Add On-Chip Controller (OCC) driver
  drivers/fsi/occ: Add in-kernel API

 .../devicetree/bindings/fsi/ibm,p9-occ.txt |  23 +
 drivers/fsi/Kconfig|  17 +
 drivers/fsi/Makefile   |   2 +
 drivers/fsi/fsi-sbefifo.c  | 921 +
 drivers/fsi/occ.c  | 790 ++
 include/linux/fsi-sbefifo.h|  30 +
 include/linux/occ.h|  27 +
 7 files changed, 1810 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
 create mode 100644 drivers/fsi/fsi-sbefifo.c
 create mode 100644 drivers/fsi/occ.c
 create mode 100644 include/linux/fsi-sbefifo.h
 create mode 100644 include/linux/occ.h

-- 
1.8.3.1



[PATCH linux 4/4] drivers/fsi/occ: Add in-kernel API

2017-06-21 Thread Eddie James
From: "Edward A. James" 

Add an in-kernel read/write API with exported functions. This is
necessary for other drivers which want to directly interact with the
OCC. Also parse the OCC device tree node for child nodes and create
child platform devices accordingly.

Signed-off-by: Edward A. James 
---
 .../devicetree/bindings/fsi/ibm,p9-occ.txt |   8 ++
 drivers/fsi/occ.c  | 140 +
 include/linux/occ.h|  27 
 3 files changed, 154 insertions(+), 21 deletions(-)
 create mode 100644 include/linux/occ.h

diff --git a/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt 
b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
index 88002b9..71afba3 100644
--- a/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
+++ b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
@@ -6,10 +6,18 @@ Required properties:
 
 Optional properties:
  - reg = ;: index for the processor this OCC device is on
+ -: Drivers for devices which communicate with
+ this OCC. Child nodes have no required or
+ optional properties that the OCC driver will
+ use.
 
 Examples:
 
 occ@1 {
 compatible = "ibm,p9-occ";
 reg = <1>;
+
+   occ-hwmon@1 {
+   compatible = "ibm,p9-occ-hwmon";
+   };
 };
diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
index a1e6e9b..c964d9e 100644
--- a/drivers/fsi/occ.c
+++ b/drivers/fsi/occ.c
@@ -16,6 +16,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -131,33 +132,43 @@ static void occ_enqueue_xfr(struct occ_xfr *xfr)
queue_work(occ_wq, &occ->work);
 }
 
-static int occ_open(struct inode *inode, struct file *file)
+static struct occ_client *occ_open_common(struct occ *occ, unsigned long flags)
 {
-   struct miscdevice *mdev = file->private_data;
-   struct occ *occ = to_occ(mdev);
struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
 
if (!client)
-   return -ENOMEM;
+   return NULL;
 
client->occ = occ;
spin_lock_init(&client->lock);
init_waitqueue_head(&client->wait);
 
-   if (file->f_flags & O_NONBLOCK)
+   if (flags & O_NONBLOCK)
set_bit(CLIENT_NONBLOCKING, &client->flags);
 
+   return client;
+}
+
+static int occ_open(struct inode *inode, struct file *file)
+{
+   struct occ_client *client;
+   struct miscdevice *mdev = file->private_data;
+   struct occ *occ = to_occ(mdev);
+
+   client = occ_open_common(occ, file->f_flags);
+   if (!client)
+   return -ENOMEM;
+
file->private_data = client;
 
return 0;
 }
 
-static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
-   loff_t *offset)
+static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf,
+  char *kbuf, size_t len)
 {
int rc;
size_t bytes;
-   struct occ_client *client = file->private_data;
struct occ_xfr *xfr = &client->xfr;
 
if (len > OCC_SRAM_BYTES)
@@ -207,10 +218,15 @@ static ssize_t occ_read(struct file *file, char __user 
*buf, size_t len,
goto done;
}
 
-   if (copy_to_user(buf, &xfr->buf[client->read_offset], bytes)) {
-   rc = -EFAULT;
-   goto done;
-   }
+   bytes = min(len, xfr->resp_data_length - client->read_offset);
+   if (ubuf) {
+   if (copy_to_user(ubuf, &xfr->buf[client->read_offset],
+bytes)) {
+   rc = -EFAULT;
+   goto done;
+   }
+   } else
+   memcpy(kbuf, &xfr->buf[client->read_offset], bytes);
 
client->read_offset += bytes;
 
@@ -225,13 +241,21 @@ static ssize_t occ_read(struct file *file, char __user 
*buf, size_t len,
return rc;
 }
 
-static ssize_t occ_write(struct file *file, const char __user *buf,
-size_t len, loff_t *offset)
+static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
+   loff_t *offset)
+{
+   struct occ_client *client = file->private_data;
+
+   return occ_read_common(client, buf, NULL, len);
+}
+
+static ssize_t occ_write_common(struct occ_client *client,
+   const char __user *ubuf, const char *kbuf,
+   size_t len)
 {
int rc;
unsigned int i;
u16 data_length, checksum = 0;
-   struct occ_client *client = file->private_data;
struct occ_xfr *xfr = &client->xfr;
 
if (len > (OCC_CMD_DATA_BYTES + 3) || len < 3)
@@ -252,10 +276,13 @@ static ssize_t occ_write(struct file *file, const char 
__user *buf,
 * bytes 1-2: data length (msb first)
 * bytes 3-n: data
  

[PATCH linux 1/4] drivers/fsi: Add SBEFIFO FSI client device driver

2017-06-21 Thread Eddie James
efifo->fsi_dev->dev,
+   "Found data in upstream fifo\n");
+   return -EIO;
+   }
+
+   ret = sbefifo_inw(sbefifo, SBEFIFO_DWN | SBEFIFO_STS, &sts);
+   if (ret)
+   return ret;
+
+   if (!(sts & SBEFIFO_EMPTY)) {
+   dev_err(&sbefifo->fsi_dev->dev,
+   "Found data in downstream fifo\n");
+   return -EIO;
+   }
+
+   sbefifo->mdev.minor = MISC_DYNAMIC_MINOR;
+   sbefifo->mdev.fops = &sbefifo_fops;
+   sbefifo->mdev.name = sbefifo->name;
+   sbefifo->mdev.parent = dev;
+   spin_lock_init(&sbefifo->lock);
+   kref_init(&sbefifo->kref);
+
+   sbefifo->idx = ida_simple_get(&sbefifo_ida, 1, INT_MAX, GFP_KERNEL);
+   snprintf(sbefifo->name, sizeof(sbefifo->name), "sbefifo%d",
+sbefifo->idx);
+   init_waitqueue_head(&sbefifo->wait);
+   INIT_LIST_HEAD(&sbefifo->xfrs);
+
+   /* This bit of silicon doesn't offer any interrupts... */
+   setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
+   (unsigned long)sbefifo);
+
+   list_add(&sbefifo->link, &sbefifo_fifos);
+
+   return misc_register(&sbefifo->mdev);
+}
+
+static int sbefifo_remove(struct device *dev)
+{
+   struct fsi_device *fsi_dev = to_fsi_dev(dev);
+   struct sbefifo *sbefifo, *sbefifo_tmp;
+   struct sbefifo_xfr *xfr;
+
+   list_for_each_entry_safe(sbefifo, sbefifo_tmp, &sbefifo_fifos, link) {
+   if (sbefifo->fsi_dev != fsi_dev)
+   continue;
+
+   misc_deregister(&sbefifo->mdev);
+   list_del(&sbefifo->link);
+   ida_simple_remove(&sbefifo_ida, sbefifo->idx);
+
+   if (del_timer_sync(&sbefifo->poll_timer))
+   sbefifo_put(sbefifo);
+
+   spin_lock(&sbefifo->lock);
+   list_for_each_entry(xfr, &sbefifo->xfrs, xfrs)
+   kfree(xfr);
+   spin_unlock(&sbefifo->lock);
+
+   WRITE_ONCE(sbefifo->rc, -ENODEV);
+
+   wake_up(&sbefifo->wait);
+   sbefifo_put(sbefifo);
+   }
+
+   return 0;
+}
+
+static struct fsi_device_id sbefifo_ids[] = {
+   {
+   .engine_type = FSI_ENGID_SBE,
+   .version = FSI_VERSION_ANY,
+   },
+   { 0 }
+};
+
+static struct fsi_driver sbefifo_drv = {
+   .id_table = sbefifo_ids,
+   .drv = {
+   .name = DEVICE_NAME,
+   .bus = &fsi_bus_type,
+   .probe = sbefifo_probe,
+   .remove = sbefifo_remove,
+   }
+};
+
+static int sbefifo_init(void)
+{
+   INIT_LIST_HEAD(&sbefifo_fifos);
+   return fsi_driver_register(&sbefifo_drv);
+}
+
+static void sbefifo_exit(void)
+{
+   fsi_driver_unregister(&sbefifo_drv);
+
+   ida_destroy(&sbefifo_ida);
+}
+
+module_init(sbefifo_init);
+module_exit(sbefifo_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Brad Bishop ");
+MODULE_AUTHOR("Eddie James ");
+MODULE_DESCRIPTION("Linux device interface to the POWER self boot engine");
-- 
1.8.3.1



[PATCH linux 3/4] drivers/fsi: Add On-Chip Controller (OCC) driver

2017-06-21 Thread Eddie James
+   empty = list_empty(&occ->xfrs);
+   canceled = test_bit(XFR_CANCELED, &xfr->flags);
+   spin_unlock(&occ->list_lock);
+
+   if (waiting)
+   wake_up_interruptible(&client->wait);
+   else if (canceled)
+   kfree(client);
+
+   if (!empty)
+   goto again;
+}
+
+static int occ_probe(struct platform_device *pdev)
+{
+   int rc;
+   u32 reg;
+   struct occ *occ;
+   struct device *dev = &pdev->dev;
+
+   occ = devm_kzalloc(dev, sizeof(*occ), GFP_KERNEL);
+   if (!occ)
+   return -ENOMEM;
+
+   occ->sbefifo = dev->parent;
+   INIT_LIST_HEAD(&occ->xfrs);
+   spin_lock_init(&occ->list_lock);
+   mutex_init(&occ->occ_lock);
+   INIT_WORK(&occ->work, occ_worker);
+
+   if (dev->of_node) {
+   rc = of_property_read_u32(dev->of_node, "reg", ®);
+   if (!rc) {
+   /* make sure we don't have a duplicate from dts */
+   occ->idx = ida_simple_get(&occ_ida, reg, reg + 1,
+ GFP_KERNEL);
+   if (occ->idx < 0)
+   occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX,
+ GFP_KERNEL);
+   } else
+   occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX,
+ GFP_KERNEL);
+   } else
+   occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL);
+
+   platform_set_drvdata(pdev, occ);
+
+   snprintf(occ->name, sizeof(occ->name), "occ%d", occ->idx);
+   occ->mdev.fops = &occ_fops;
+   occ->mdev.minor = MISC_DYNAMIC_MINOR;
+   occ->mdev.name = occ->name;
+   occ->mdev.parent = dev;
+
+   rc = misc_register(&occ->mdev);
+   if (rc) {
+   dev_err(dev, "failed to register miscdevice\n");
+   return rc;
+   }
+
+   return 0;
+}
+
+static int occ_remove(struct platform_device *pdev)
+{
+   struct occ *occ = platform_get_drvdata(pdev);
+
+   flush_work(&occ->work);
+   misc_deregister(&occ->mdev);
+   ida_simple_remove(&occ_ida, occ->idx);
+
+   return 0;
+}
+
+static const struct of_device_id occ_match[] = {
+   { .compatible = "ibm,p9-occ" },
+   { },
+};
+
+static struct platform_driver occ_driver = {
+   .driver = {
+   .name = "occ",
+   .of_match_table = occ_match,
+   },
+   .probe  = occ_probe,
+   .remove = occ_remove,
+};
+
+static int occ_init(void)
+{
+   occ_wq = create_singlethread_workqueue("occ");
+   if (!occ_wq)
+   return -ENOMEM;
+
+   return platform_driver_register(&occ_driver);
+}
+
+static void occ_exit(void)
+{
+   destroy_workqueue(occ_wq);
+
+   platform_driver_unregister(&occ_driver);
+
+   ida_destroy(&occ_ida);
+}
+
+module_init(occ_init);
+module_exit(occ_exit);
+
+MODULE_AUTHOR("Eddie James ");
+MODULE_DESCRIPTION("BMC P9 OCC driver");
+MODULE_LICENSE("GPL");
-- 
1.8.3.1



[PATCH linux 2/4] drivers/fsi/sbefifo: Add in-kernel API

2017-06-21 Thread Eddie James
From: "Edward A. James" 

Refactor the user interface of the SBEFIFO driver to allow for an
in-kernel read/write API. Add exported functions for other drivers to
call, and add an include file with those functions. Also parse the
device tree for child nodes and create child platform devices
accordingly.

Signed-off-by: Edward A. James 
---
 drivers/fsi/fsi-sbefifo.c   | 141 +---
 include/linux/fsi-sbefifo.h |  30 ++
 2 files changed, 150 insertions(+), 21 deletions(-)
 create mode 100644 include/linux/fsi-sbefifo.h

diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index fca277c..1038f6c 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -15,9 +15,12 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -82,6 +85,7 @@ struct sbefifo_client {
struct list_head xfrs;
struct sbefifo *dev;
struct kref kref;
+   unsigned long f_flags;
 };
 
 static struct list_head sbefifo_fifos;
@@ -509,6 +513,7 @@ static int sbefifo_open(struct inode *inode, struct file 
*file)
return -ENOMEM;
 
file->private_data = client;
+   client->f_flags = file->f_flags;
 
return 0;
 }
@@ -533,19 +538,18 @@ static unsigned int sbefifo_poll(struct file *file, 
poll_table *wait)
return mask;
 }
 
-static ssize_t sbefifo_read(struct file *file, char __user *buf, size_t len,
-   loff_t *offset)
+static ssize_t sbefifo_read_common(struct sbefifo_client *client,
+  char __user *ubuf, char *kbuf, size_t len)
 {
-   struct sbefifo_client *client = file->private_data;
struct sbefifo *sbefifo = client->dev;
struct sbefifo_xfr *xfr;
-   ssize_t ret = 0;
size_t n;
+   ssize_t ret = 0;
 
if ((len >> 2) << 2 != len)
return -EINVAL;
 
-   if ((file->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
+   if ((client->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
return -EAGAIN;
 
sbefifo_get_client(client);
@@ -564,10 +568,13 @@ static ssize_t sbefifo_read(struct file *file, char 
__user *buf, size_t len,
 
n = min_t(size_t, n, len);
 
-   if (copy_to_user(buf, READ_ONCE(client->rbuf.rpos), n)) {
-   sbefifo_put_client(client);
-   return -EFAULT;
-   }
+   if (ubuf) {
+   if (copy_to_user(ubuf, READ_ONCE(client->rbuf.rpos), n)) {
+   sbefifo_put_client(client);
+   return -EFAULT;
+   }
+   } else
+   memcpy(kbuf, READ_ONCE(client->rbuf.rpos), n);
 
if (sbefifo_buf_readnb(&client->rbuf, n)) {
xfr = sbefifo_client_next_xfr(client);
@@ -590,10 +597,18 @@ static ssize_t sbefifo_read(struct file *file, char 
__user *buf, size_t len,
return n;
 }
 
-static ssize_t sbefifo_write(struct file *file, const char __user *buf,
-size_t len, loff_t *offset)
+static ssize_t sbefifo_read(struct file *file, char __user *buf, size_t len,
+   loff_t *offset)
 {
struct sbefifo_client *client = file->private_data;
+
+   return sbefifo_read_common(client, buf, NULL, len);
+}
+
+static ssize_t sbefifo_write_common(struct sbefifo_client *client,
+   const char __user *ubuf, const char *kbuf,
+   size_t len)
+{
struct sbefifo *sbefifo = client->dev;
struct sbefifo_buf *wbuf = &client->wbuf;
struct sbefifo_xfr *xfr;
@@ -611,7 +626,7 @@ static ssize_t sbefifo_write(struct file *file, const char 
__user *buf,
spin_lock_irq(&sbefifo->lock);
xfr = sbefifo_next_xfr(sbefifo);
 
-   if ((file->f_flags & O_NONBLOCK) && xfr && n < len) {
+   if ((client->f_flags & O_NONBLOCK) && xfr && n < len) {
spin_unlock_irq(&sbefifo->lock);
return -EAGAIN;
}
@@ -650,18 +665,24 @@ static ssize_t sbefifo_write(struct file *file, const 
char __user *buf,
 
n = min_t(size_t, n, len);
 
-   if (copy_from_user(READ_ONCE(wbuf->wpos), buf, n)) {
-   set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
-   sbefifo_get(sbefifo);
-   if (mod_timer(&sbefifo->poll_timer, jiffies))
-   sbefifo_put(sbefifo);
-   sbefifo_put_client(client);
-   return -EFAULT;
+   if (ubuf) {
+   if (copy_from_user(READ_ONCE(wbuf->wpos), ubuf, n)) {
+   set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
+   sbefifo_get(sbefifo);
+   if (mod_timer(&sbefifo->poll_timer, jiffies))
+

[prefix=PATCH] drivers: pmbus: core: Prevent calling pmbus_set_page with negative page

2017-10-25 Thread Eddie James
From: "Edward A. James" 

For devices with word data registers, the pmbus core may call read/write
word data functions with a page value of -1, in order to perform the
operation without setting the page. However, the read/write word data
functions accept only unsigned 8-bit page numbers, resulting in setting
a page of 0xFF in this situation. This may result in errors or undefined
behavior of some devices (specifically the ir35221, which allows the
page to be set to 0xFF, but some subsequent operations to read registers
may fail).

Signed-off-by: Edward A. James 
---
 drivers/hwmon/pmbus/pmbus.h  |  4 ++--
 drivers/hwmon/pmbus/pmbus_core.c | 29 ++---
 2 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 4efa2bd..4a16da6 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -405,8 +405,8 @@ struct pmbus_driver_info {
 
 void pmbus_clear_cache(struct i2c_client *client);
 int pmbus_set_page(struct i2c_client *client, u8 page);
-int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
-int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 
word);
+int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg);
+int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, u16 
word);
 int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
 int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
 int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 302f0ae..6d6e030 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -186,13 +186,16 @@ static int _pmbus_write_byte(struct i2c_client *client, 
int page, u8 value)
return pmbus_write_byte(client, page, value);
 }
 
-int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)
+int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
+ u16 word)
 {
int rv;
 
-   rv = pmbus_set_page(client, page);
-   if (rv < 0)
-   return rv;
+   if (page >= 0) {
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
+   }
 
return i2c_smbus_write_word_data(client, reg, word);
 }
@@ -219,13 +222,15 @@ static int _pmbus_write_word_data(struct i2c_client 
*client, int page, int reg,
return pmbus_write_word_data(client, page, reg, word);
 }
 
-int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
+int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg)
 {
int rv;
 
-   rv = pmbus_set_page(client, page);
-   if (rv < 0)
-   return rv;
+   if (page >= 0) {
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
+   }
 
return i2c_smbus_read_word_data(client, reg);
 }
@@ -269,9 +274,11 @@ int pmbus_write_byte_data(struct i2c_client *client, int 
page, u8 reg, u8 value)
 {
int rv;
 
-   rv = pmbus_set_page(client, page);
-   if (rv < 0)
-   return rv;
+   if (page >= 0) {
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
+   }
 
return i2c_smbus_write_byte_data(client, reg, value);
 }
-- 
1.8.3.1



Re: drivers (pmbus): ir35221: Set PMBUS_PAGE before reading id and model

2017-10-25 Thread Eddie James



On 10/23/2017 05:12 PM, Guenter Roeck wrote:

On Mon, Oct 23, 2017 at 10:23:02AM -0500, Eddie James wrote:


On 10/21/2017 11:10 AM, Guenter Roeck wrote:

On Thu, Oct 19, 2017 at 03:57:08PM -0500, eaja...@linux.vnet.ibm.com wrote:

From: "Edward A. James" 

The MFR_ID and MFR_MODEL, which are manually read before probing the
pmbus core,  are only valid for the two pages that the ir35221 has
available. Since we don't know the state of the device when we start
probing, set the page number first before reading id and model.


If the device only has two pages, it is highly unlikely that it is possible
to select another page which is not supported; any attempts to do so should
result in a command error.

Is this theory or based on an actual problem observed ? If so, it will require
some additional explanation.

Hi,

Yes, I have observed this with a physical ir35221 device off an i2c bus. It
seems that there are some places in the PMBUS core that manage to set the
page number to 0xFF due to pmbus_set_page being called with page=-1.

This would be bug(s) in the PMBus core which need(s) to be fixed.


pmbus_set_page doesn't check the CML register, so if the i2c op succeeds, it
will be a success. I can also set it to anything with i2cset tool without
error.

I would also like to fix calling pmbus_set_page with -1, and I will send up
a patch if/when I have time. But I felt it makes sense to also ensure the
page is set correctly in the ir35221 driver, since it's an unknown state at
probe time, and this device appears a bit "weird" in that it doesn't return
an error if it's an unsupported page.


With that logic, one could argue that we have to reprogram the entire chip
because, after all, soneone could have messed it up completely using i2cset.
And the same would be true for all the chips supported by any driver.


Good point... Consider this patch abandoned. I have sent up a patch for 
pmbus core.


Thanks,
Eddie



I'd rather focus on fixing the PMBus core if it really tries to set page #255.

Guenter


Thanks,
Eddie


Thanks,
Guenter


Signed-off-by: Edward A. James 
---
  drivers/hwmon/pmbus/ir35221.c | 6 ++
  1 file changed, 6 insertions(+)

diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c
index 8b906b4..9a53930 100644
--- a/drivers/hwmon/pmbus/ir35221.c
+++ b/drivers/hwmon/pmbus/ir35221.c
@@ -267,6 +267,12 @@ static int ir35221_probe(struct i2c_client *client,
| I2C_FUNC_SMBUS_READ_BLOCK_DATA))
return -ENODEV;
+   ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to set PMBUS_PAGE\n");
+   return ret;
+   }
+
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0) {
dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");




Re: [prefix=PATCH] drivers: pmbus: core: Prevent calling pmbus_set_page with negative page

2017-10-25 Thread Eddie James



On 10/25/2017 04:12 PM, Guenter Roeck wrote:

On Wed, Oct 25, 2017 at 02:52:53PM -0500, Eddie James wrote:

From: "Edward A. James" 

For devices with word data registers, the pmbus core may call read/write
word data functions with a page value of -1, in order to perform the
operation without setting the page. However, the read/write word data
functions accept only unsigned 8-bit page numbers, resulting in setting
a page of 0xFF in this situation. This may result in errors or undefined
behavior of some devices (specifically the ir35221, which allows the
page to be set to 0xFF, but some subsequent operations to read registers
may fail).


Good catch.

Subject came off a bit odd.

[prefix=PATCH] drivers: pmbus: core: Prevent ...

Please use "[PATCH] hwmon: (pmbus/core) Prevent ..."


Yea I hit = instead of - in "--subject-prefix"...



This me wonder if we should move the check into pmbus_set_page()
instead.

int pmbus_set_page(struct i2c_client *client, int page)
{
...
if (page >= 0 && page != data->currpage) {
...
}

What do you think ?


Yea that works well too, and reduces the code size. Though I suppose it 
makes the interface slightly less intuitive, as someone might think they 
can pass a value larger than u8. At least for the read/write functions, 
a negative value has a meaning - "don't change the page."


Up to you, either way. I'm happy to change it.

Thanks,
Eddie



Thanks,
Guenter


Signed-off-by: Edward A. James 
---
  drivers/hwmon/pmbus/pmbus.h  |  4 ++--
  drivers/hwmon/pmbus/pmbus_core.c | 29 ++---
  2 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 4efa2bd..4a16da6 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -405,8 +405,8 @@ struct pmbus_driver_info {
  
  void pmbus_clear_cache(struct i2c_client *client);

  int pmbus_set_page(struct i2c_client *client, u8 page);
-int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
-int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 
word);
+int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg);
+int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, u16 
word);
  int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
  int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
  int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 302f0ae..6d6e030 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -186,13 +186,16 @@ static int _pmbus_write_byte(struct i2c_client *client, 
int page, u8 value)
return pmbus_write_byte(client, page, value);
  }
  
-int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)

+int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
+ u16 word)
  {
int rv;
  
-	rv = pmbus_set_page(client, page);

-   if (rv < 0)
-   return rv;
+   if (page >= 0) {
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
+   }
  
  	return i2c_smbus_write_word_data(client, reg, word);

  }
@@ -219,13 +222,15 @@ static int _pmbus_write_word_data(struct i2c_client 
*client, int page, int reg,
return pmbus_write_word_data(client, page, reg, word);
  }
  
-int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)

+int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg)
  {
int rv;
  
-	rv = pmbus_set_page(client, page);

-   if (rv < 0)
-   return rv;
+   if (page >= 0) {
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
+   }
  
  	return i2c_smbus_read_word_data(client, reg);

  }
@@ -269,9 +274,11 @@ int pmbus_write_byte_data(struct i2c_client *client, int 
page, u8 reg, u8 value)
  {
int rv;
  
-	rv = pmbus_set_page(client, page);

-   if (rv < 0)
-   return rv;
+   if (page >= 0) {
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
+   }
  
  	return i2c_smbus_write_byte_data(client, reg, value);

  }
--
1.8.3.1





[PATCH] drivers (pmbus): ir35221: Set PMBUS_PAGE before reading id and model

2017-10-19 Thread Eddie James
From: "Edward A. James" 

The MFR_ID and MFR_MODEL, which are manually read before probing the
pmbus core,  are only valid for the two pages that the ir35221 has
available. Since we don't know the state of the device when we start
probing, set the page number first before reading id and model.

Signed-off-by: Edward A. James 
---
 drivers/hwmon/pmbus/ir35221.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c
index 8b906b4..9a53930 100644
--- a/drivers/hwmon/pmbus/ir35221.c
+++ b/drivers/hwmon/pmbus/ir35221.c
@@ -267,6 +267,12 @@ static int ir35221_probe(struct i2c_client *client,
| I2C_FUNC_SMBUS_READ_BLOCK_DATA))
return -ENODEV;
 
+   ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to set PMBUS_PAGE\n");
+   return ret;
+   }
+
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0) {
dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
-- 
1.8.3.1



[PATCH v2] hwmon: (pmbus/core): Prevent unintentional setting of page to 0xFF

2017-10-27 Thread Eddie James
From: "Edward A. James" 

The pmbus core may call read/write word data functions with a page value
of -1, intending to perform the operation without setting the page.
However, the read/write word data functions accept only unsigned 8-bit
page numbers, and therefore cannot check for negative page number to
avoid setting the page. This results in setting the page number to 0xFF.
This may result in errors or undefined behavior of some devices
(specifically the ir35221, which allows the page to be set to 0xFF,
but some subsequent operations to read registers may fail).

Switch the pmbus_set_page page parameter to an integer and perform the
check for negative page there. Make read/write functions consistent in
accepting an integer page number parameter.

Signed-off-by: Edward A. James 
---

Changes since v1:
 * Move check for negative page to pmbus_set_page and change pmbus_set_page
   page parameter to int.

 drivers/hwmon/pmbus/pmbus.h  |  6 +++---
 drivers/hwmon/pmbus/pmbus_core.c | 25 +++--
 2 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 4efa2bd..fa613bd 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -404,9 +404,9 @@ struct pmbus_driver_info {
 /* Function declarations */
 
 void pmbus_clear_cache(struct i2c_client *client);
-int pmbus_set_page(struct i2c_client *client, u8 page);
-int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
-int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 
word);
+int pmbus_set_page(struct i2c_client *client, int page);
+int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg);
+int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, u16 
word);
 int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
 int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
 int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 302f0ae..52a58b8 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -136,13 +136,13 @@ void pmbus_clear_cache(struct i2c_client *client)
 }
 EXPORT_SYMBOL_GPL(pmbus_clear_cache);
 
-int pmbus_set_page(struct i2c_client *client, u8 page)
+int pmbus_set_page(struct i2c_client *client, int page)
 {
struct pmbus_data *data = i2c_get_clientdata(client);
int rv = 0;
int newpage;
 
-   if (page != data->currpage) {
+   if (page >= 0 && page != data->currpage) {
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
if (newpage != page)
@@ -158,11 +158,9 @@ int pmbus_write_byte(struct i2c_client *client, int page, 
u8 value)
 {
int rv;
 
-   if (page >= 0) {
-   rv = pmbus_set_page(client, page);
-   if (rv < 0)
-   return rv;
-   }
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
 
return i2c_smbus_write_byte(client, value);
 }
@@ -186,7 +184,8 @@ static int _pmbus_write_byte(struct i2c_client *client, int 
page, u8 value)
return pmbus_write_byte(client, page, value);
 }
 
-int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)
+int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
+ u16 word)
 {
int rv;
 
@@ -219,7 +218,7 @@ static int _pmbus_write_word_data(struct i2c_client 
*client, int page, int reg,
return pmbus_write_word_data(client, page, reg, word);
 }
 
-int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
+int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg)
 {
int rv;
 
@@ -255,11 +254,9 @@ int pmbus_read_byte_data(struct i2c_client *client, int 
page, u8 reg)
 {
int rv;
 
-   if (page >= 0) {
-   rv = pmbus_set_page(client, page);
-   if (rv < 0)
-   return rv;
-   }
+   rv = pmbus_set_page(client, page);
+   if (rv < 0)
+   return rv;
 
return i2c_smbus_read_byte_data(client, reg);
 }
-- 
1.8.3.1



[PATCH v4 4/9] hwmon: Add On-Chip Controller (OCC) hwmon driver

2018-07-11 Thread Eddie James
The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.

This driver provides a hwmon interface to the OCC from a service
processor (e.g. a BMC). The driver supports both POWER8 and POWER9 OCCs.
Communications with the POWER8 OCC are established over standard I2C
bus. The driver communicates with the POWER9 OCC through the FSI-based
OCC driver, which handles the lower-level communication details.

This patch lays out the structure of the OCC hwmon driver. There are two
platform drivers, one each for P8 and P9 OCCs. These are probed through
the I2C tree and the FSI-based OCC driver, respectively. The patch also
defines the first common structures and methods between the two OCC
versions.

Signed-off-by: Eddie James 
---
 drivers/hwmon/Kconfig  |  2 ++
 drivers/hwmon/Makefile |  1 +
 drivers/hwmon/occ/Kconfig  | 28 +
 drivers/hwmon/occ/Makefile | 11 +++
 drivers/hwmon/occ/common.c | 50 +
 drivers/hwmon/occ/common.h | 43 +
 drivers/hwmon/occ/p8_i2c.c | 71 +
 drivers/hwmon/occ/p9_sbe.c | 78 ++
 8 files changed, 284 insertions(+)
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/common.c
 create mode 100644 drivers/hwmon/occ/common.h
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 create mode 100644 drivers/hwmon/occ/p9_sbe.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a4e5d3c..9b3871e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1293,6 +1293,8 @@ config SENSORS_NSA320
  This driver can also be built as a module. If so, the module
  will be called nsa320-hwmon.
 
+source drivers/hwmon/occ/Kconfig
+
 config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 93f7f41..f5c7b44 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -178,6 +178,7 @@ obj-$(CONFIG_SENSORS_WM831X)+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)   += wm8350-hwmon.o
 obj-$(CONFIG_SENSORS_XGENE)+= xgene-hwmon.o
 
+obj-$(CONFIG_SENSORS_OCC)  += occ/
 obj-$(CONFIG_PMBUS)+= pmbus/
 
 ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
new file mode 100644
index 000..9579b63
--- /dev/null
+++ b/drivers/hwmon/occ/Kconfig
@@ -0,0 +1,28 @@
+#
+# On-Chip Controller configuration
+#
+
+config SENSORS_OCC
+   tristate "POWER On-Chip Controller"
+   help
+This option enables support for monitoring a variety of system sensors
+provided by the On-Chip Controller (OCC) on a POWER processor.
+
+This driver can also be built as a module. If so, the module will be
+called occ-hwmon.
+
+config SENSORS_OCC_P8_I2C
+   bool "POWER8 OCC through I2C"
+   depends on I2C && SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the OCC
+on a POWER8 processor. Communications with the OCC are established
+through I2C bus.
+
+config SENSORS_OCC_P9_SBE
+   bool "POWER9 OCC through SBE"
+   depends on FSI_OCC && SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the OCC
+on a POWER9 processor. Communications with the OCC are established
+through SBEFIFO on an FSI bus.
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
new file mode 100644
index 000..ab5c3e9
--- /dev/null
+++ b/drivers/hwmon/occ/Makefile
@@ -0,0 +1,11 @@
+occ-hwmon-objs := common.o
+
+ifeq ($(CONFIG_SENSORS_OCC_P9_SBE), y)
+occ-hwmon-objs += p9_sbe.o
+endif
+
+ifeq ($(CONFIG_SENSORS_OCC_P8_I2C), y)
+occ-hwmon-objs += p8_i2c.o
+endif
+
+obj-$(CONFIG_SENSORS_OCC) += occ-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
new file mode 100644
index 000..1fd3453
--- /dev/null
+++ b/drivers/hwmon/occ/common.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OCC hwmon driver common functionality
+ *
+ * Copyright (C) IBM Corporation 2018
+ *
+ * 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 
+
+#include "common.h"
+
+static int occ_poll(struct occ *occ)
+{
+   u16 checksum = occ->poll_cmd_data + 1;
+   u8 cmd[8];
+
+   /* big endian */
+   cmd[0] = 0; /* sequence number */
+   

Re: [PATCH] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-10 Thread Eddie James



On 01/09/2018 04:50 PM, Guenter Roeck wrote:

On Tue, Jan 09, 2018 at 04:05:24PM -0600, Eddie James wrote:

This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---
  drivers/hwmon/pmbus/ibm-cffps.c | 90 +
  1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..1e95dea 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
  #include 
  #include 
  #include 
+#include 
  #include 
  #include 
  #include 
@@ -25,6 +26,7 @@
  #define CFFPS_CCIN_CMD0xBD
  #define CFFPS_FW_CMD_START0xFA
  #define CFFPS_FW_NUM_BYTES4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
  
  #define CFFPS_INPUT_HISTORY_CMD			0xD6

  #define CFFPS_INPUT_HISTORY_SIZE  100
@@ -39,6 +41,11 @@
  #define CFFPS_MFR_VAUX_FAULT  BIT(6)
  #define CFFPS_MFR_CURRENT_SHARE_WARNING   BIT(7)
  
+#define CFFPS_LED_BLINKBIT(0)

+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
  enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,9 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
  
  	int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];

+
+   u8 led_state;
+   struct led_classdev led;
  };
  
  #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])

@@ -258,6 +268,51 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
  }
  
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,

+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
  static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -277,6 +332,8 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
  {
int i, rc;
+   char *led_name;
+   size_t name_length;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -286,6 +343,31 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (rc)
return rc;
  
+	/* client name, '-', 8 chars for addr, and null */

+   name_length = strlen(client->name) + 10;
+
+   /* Don't fail the probe if we can't create led devices */
+   psu = devm_kzalloc(&client->dev, sizeof(*psu) + name_length,
+  GFP_KERNEL);
+   if (!psu)
+   return 0;

... and no debugfs either ?


Well we need that struct allocated for debugfs as well. I'll clarify the 
comment.





+
+   psu->client = client;
+   mutex_init(&psu->input_history.update_lock);
+   psu->input_history.last_update = jiffies - HZ;
+
+   led_name = (void *)(&psu[1]);
+   snprintf(led_name, name_length, "%s-%x", client->name, client->addr);
+   psu->led.name = led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(&client->dev, 

[PATCH v2] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-10 Thread Eddie James
This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---

 Changes since v1:
 - move led registration into a separate function.
 - improve comments.

 drivers/hwmon/pmbus/ibm-cffps.c | 99 +
 1 file changed, 91 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..cd9f685 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -25,6 +26,7 @@
 #define CFFPS_CCIN_CMD 0xBD
 #define CFFPS_FW_CMD_START 0xFA
 #define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
 
 #define CFFPS_INPUT_HISTORY_CMD0xD6
 #define CFFPS_INPUT_HISTORY_SIZE   100
@@ -39,6 +41,11 @@
 #define CFFPS_MFR_VAUX_FAULT   BIT(6)
 #define CFFPS_MFR_CURRENT_SHARE_WARNINGBIT(7)
 
+#define CFFPS_LED_BLINKBIT(0)
+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
 enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,9 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
 
int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
+
+   u8 led_state;
+   struct led_classdev led;
 };
 
 #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
@@ -258,6 +268,69 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
 }
 
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
+static void ibm_cffps_create_led_class(size_t name_size, struct ibm_cffps *psu)
+{
+   int rc;
+   struct i2c_client *client = psu->client;
+   struct device *dev = &client->dev;
+   char *led_name = (void *)(&psu[1]);
+
+   snprintf(led_name, name_size, "%s-%x", client->name, client->addr);
+   psu->led.name = led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(dev, &psu->led);
+   if (rc)
+   dev_warn(dev, "failed to register led class: %d\n", rc);
+}
+
 static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -277,6 +350,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
 {
int i, rc;
+   size_t name_size;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -286,6 +360,23 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (rc)
return rc;
 
+   /* client name, '-', 8 chars for addr, and null terminator */
+   name_size = strlen(client->name) + 10;
+
+   /*
+* Don't fail the probe if there isn't enough memory for leds and
+* debugfs.
+*/
+   psu = devm_kzalloc(&client->dev, sizeof(*psu) + name_size, GFP_KER

Re: [PATCH v2] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-11 Thread Eddie James



On 01/10/2018 12:16 PM, Guenter Roeck wrote:

On Wed, Jan 10, 2018 at 11:29:43AM -0600, Eddie James wrote:

This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---

  Changes since v1:
  - move led registration into a separate function.
  - improve comments.

  drivers/hwmon/pmbus/ibm-cffps.c | 99 +
  1 file changed, 91 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..cd9f685 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
  #include 
  #include 
  #include 
+#include 
  #include 
  #include 
  #include 
@@ -25,6 +26,7 @@
  #define CFFPS_CCIN_CMD0xBD
  #define CFFPS_FW_CMD_START0xFA
  #define CFFPS_FW_NUM_BYTES4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
  
  #define CFFPS_INPUT_HISTORY_CMD			0xD6

  #define CFFPS_INPUT_HISTORY_SIZE  100
@@ -39,6 +41,11 @@
  #define CFFPS_MFR_VAUX_FAULT  BIT(6)
  #define CFFPS_MFR_CURRENT_SHARE_WARNING   BIT(7)
  
+#define CFFPS_LED_BLINKBIT(0)

+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
  enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,9 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
  
  	int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];

+
+   u8 led_state;
+   struct led_classdev led;
  };
  
  #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])

@@ -258,6 +268,69 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
  }
  
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,

+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
+static void ibm_cffps_create_led_class(size_t name_size, struct ibm_cffps *psu)
+{
+   int rc;
+   struct i2c_client *client = psu->client;
+   struct device *dev = &client->dev;
+   char *led_name = (void *)(&psu[1]);
+

I really dislike this hack. Why not allocate a sufficient
fixed length, say, 32 bytes, and just use it ? If you want
to be really wasteful, use 48 or 64 bytes; that is still better
than this code.


That's fair, I found this trick in some sony hid driver, but fixed 
length is much cleaner.





+   snprintf(led_name, name_size, "%s-%x", client->name, client->addr);

%02x, maybe ? Your call, though.

Thanks,
Guenter


+   psu->led.name = led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(dev, &psu->led);
+   if (rc)
+   dev_warn(dev, "failed to register led class: %d\n", rc);
+}
+
  static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -277,6 +350,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
  {
int i, rc;
+   size_t name_size;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -286,6 +360,23 @@ stat

[PATCH v3] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-11 Thread Eddie James
This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---

 Changes since v2:
 - Use fixed size buffer for led name.

 Changes since v1:
 - move led registration into a separate function.
 - improve comments.

 drivers/hwmon/pmbus/ibm-cffps.c | 96 +
 1 file changed, 88 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..93d9a9e 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -25,6 +26,7 @@
 #define CFFPS_CCIN_CMD 0xBD
 #define CFFPS_FW_CMD_START 0xFA
 #define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
 
 #define CFFPS_INPUT_HISTORY_CMD0xD6
 #define CFFPS_INPUT_HISTORY_SIZE   100
@@ -39,6 +41,11 @@
 #define CFFPS_MFR_VAUX_FAULT   BIT(6)
 #define CFFPS_MFR_CURRENT_SHARE_WARNINGBIT(7)
 
+#define CFFPS_LED_BLINKBIT(0)
+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
 enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,10 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
 
int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
+
+   char led_name[32];
+   u8 led_state;
+   struct led_classdev led;
 };
 
 #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
@@ -258,6 +269,69 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
 }
 
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
+static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
+{
+   int rc;
+   struct i2c_client *client = psu->client;
+   struct device *dev = &client->dev;
+
+   snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,
+client->addr);
+   psu->led.name = psu->led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(dev, &psu->led);
+   if (rc)
+   dev_warn(dev, "failed to register led class: %d\n", rc);
+}
+
 static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -286,6 +360,20 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (rc)
return rc;
 
+   /*
+* Don't fail the probe if there isn't enough memory for leds and
+* debugfs.
+*/
+   psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
+   if (!psu)
+   return 0;
+
+   psu->client = client;
+   mutex_init(&psu->input_history.update_lock);
+   psu->input_history.last_update = jiffies - HZ;
+
+   ibm_cffps_create_led_class(psu);
+
/* Don't fail the probe if we can't create debugfs */
debugfs = pmbus_g

[PATCH] hwmon (pmbus): cffps: Add PMBUS_SKIP_STATUS_CHECK

2018-01-08 Thread Eddie James
This power supply device regularly fails to read VOUT_MODE due to the
CML bit going high. This results in an incorrect exponent used for the
voltage data, and therefore the power supply reports incorrect voltage.
Work around this by setting the pmbus flag to skip the CML check.

Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ibm-cffps.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index de254747..2d6f4f4 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -15,6 +15,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "pmbus.h"
 
@@ -268,6 +269,10 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
.read_word_data = ibm_cffps_read_word_data,
 };
 
+static struct pmbus_platform_data ibm_cffps_pdata = {
+   .flags = PMBUS_SKIP_STATUS_CHECK,
+};
+
 static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
 {
@@ -276,6 +281,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
 
+   client->dev.platform_data = &ibm_cffps_pdata;
rc = pmbus_do_probe(client, id, &ibm_cffps_info);
if (rc)
return rc;
-- 
1.8.3.1



[PATCH] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-09 Thread Eddie James
This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ibm-cffps.c | 90 +
 1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..1e95dea 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -25,6 +26,7 @@
 #define CFFPS_CCIN_CMD 0xBD
 #define CFFPS_FW_CMD_START 0xFA
 #define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
 
 #define CFFPS_INPUT_HISTORY_CMD0xD6
 #define CFFPS_INPUT_HISTORY_SIZE   100
@@ -39,6 +41,11 @@
 #define CFFPS_MFR_VAUX_FAULT   BIT(6)
 #define CFFPS_MFR_CURRENT_SHARE_WARNINGBIT(7)
 
+#define CFFPS_LED_BLINKBIT(0)
+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
 enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,9 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
 
int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
+
+   u8 led_state;
+   struct led_classdev led;
 };
 
 #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
@@ -258,6 +268,51 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
 }
 
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
 static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -277,6 +332,8 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
 {
int i, rc;
+   char *led_name;
+   size_t name_length;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -286,6 +343,31 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (rc)
return rc;
 
+   /* client name, '-', 8 chars for addr, and null */
+   name_length = strlen(client->name) + 10;
+
+   /* Don't fail the probe if we can't create led devices */
+   psu = devm_kzalloc(&client->dev, sizeof(*psu) + name_length,
+  GFP_KERNEL);
+   if (!psu)
+   return 0;
+
+   psu->client = client;
+   mutex_init(&psu->input_history.update_lock);
+   psu->input_history.last_update = jiffies - HZ;
+
+   led_name = (void *)(&psu[1]);
+   snprintf(led_name, name_length, "%s-%x", client->name, client->addr);
+   psu->led.name = led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(&client->dev, &psu->led);
+   if (rc)
+   dev_info(&client->dev, "failed to register led class: %d\n",
+rc);
+
/* Don't fail the probe if we can't cre

Re: [PATCH] hwmon: pmbus: ibm-cffps depends on LEDS_CLASS

2018-01-15 Thread Eddie James


On Fri, Jan 12, 2018 at 06:19:00PM +0100, Guenter Roeck wrote:

On Fri, Jan 12, 2018 at 04:49:00PM +0100, Arnd Bergmann wrote:
> Building without CONFIG_LEDS_CLASS causes a link failure:
>
> drivers/hwmon/pmbus/ibm-cffps.o: In function `ibm_cffps_probe':
> ibm-cffps.c:(.text+0x4f4): undefined reference to 
`devm_of_led_classdev_register'

>
> This adds the required dependency.
>
> Fixes: f69316d62c70 ("hwmon: (pmbus) Add IBM Common Form Factor 
(CFF) power supply driver")

> Signed-off-by: Arnd Bergmann 

I wanted to let Edward decide if he wants the new dependency or 
conditional code
in the driver. Not having heard from him, I'll take your patch instead 
for now.


Thanks, yes this is a good solution for this driver. Didn't think about 
that during testing... Thanks Arnd.


Eddie



Thanks,
Guenter

> ---
>  drivers/hwmon/pmbus/Kconfig | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index 08479006c7f9..6e4298e99222 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -39,6 +39,7 @@ config SENSORS_ADM1275
>
>  config SENSORS_IBM_CFFPS
>   tristate "IBM Common Form Factor Power Supply"
> + depends on LEDS_CLASS
>   help
>    If you say yes here you get hardware monitoring support for the IBM
>    Common Form Factor power supply.
> --
> 2.9.0
>





[PATCH 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-09 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 392 +-
 1 file changed, 391 insertions(+), 1 deletion(-)

-- 
1.8.3.1



[PATCH 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-09 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 172 +-
 1 file changed, 171 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index e3a507f..297da0e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -36,6 +37,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -63,13 +65,22 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -328,6 +339,156 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
 }
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   /*
+* With the ucd90120 and ucd90124 devices, this command [MFR_STATUS]
+* is 2 bytes long (bits 0-15).  With the ucd90240 this command is 5
+* bytes long.  With all other devices, it is 4 bytes long.
+*/
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[4];
+   int ret;
+
+   /*
+* This attribute is only created for devices that return 4 bytes for
+* status_mfr, so it's safe to call with 4-byte buffer.
+*/
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+   ret);
+
+   return ret;
+   }
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static int ucd9000_debugfs_show_mfr_status_word2(void *data, u64 *val)
+{
+   struct i2c_client *client = data;
+   __be16 buffer;
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+   ret);
+
+   return ret;
+   }
+
+   *val = be16_to_cpu(buffer);
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word2,
+ucd9000_debugfs_show_mfr_status_word2, NULL,
+"%04llx\n");
+
+static int ucd9000_debugfs_show_mfr_status_word4(void *data, u64 *val)
+{
+   struct i2c_client *client = data;
+   __be32 buffer;
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+   ret);
+
+   return ret;
+   }
+
+   *val = be32_to_cpu(buffer);
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word4,
+ucd9000_debugfs_show_mfr_status_word4, NULL,
+"%08llx\n");
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_e

[PATCH 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-09 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 220 ++
 1 file changed, 220 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..e3a507f 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +36,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +58,15 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+   struct gpio_chip gpio;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +166,168 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to select GPIO %d: %d\n", offset,
+   ret);
+
+   return ret;
+   }
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+
+   return ret;
+   }
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_err(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+
+   return ret;
+   }
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(st

[PATCH 0/2] aspeed: watchdog: fix system reset and read alt-boot option

2018-03-09 Thread Eddie James
This series provides a fix to correctly set the reset mode of the control
register. Previously, configuring anything other than a full chip reset would
not reset anything when the watchdog timer expires. The series also provides a
patch to read in the existing aspeed,alt-boot boolean option from the
device-tree and set the appropriate bit in the control register.

Milton Miller (2):
  watchdog: aspeed: Fix translation of reset mode to ctrl register
  watchdog: aspeed: Allow configuring for alternate boot

 drivers/watchdog/aspeed_wdt.c | 12 +---
 1 file changed, 9 insertions(+), 3 deletions(-)

-- 
1.8.3.1



[PATCH 1/2] watchdog: aspeed: Fix translation of reset mode to ctrl register

2018-03-09 Thread Eddie James
From: Milton Miller 

Assert RESET_SYSTEM bit for any reset and set MODE field from reset
type.

The watchdog control register has a RESET_SYSTEM bit that is really
closer to activate a reset, and RESET_SYSTEM_MODE field that chooses
how much to reset.

Before this patch, a node without these optional property would do a
SOC reset, but a node with properties requesting a cpu or SOC reset
would do nothing and a node requesting a system reset would do a
SOC reset.

Fixes: b7f0b8ad25f3 ("drivers/watchdog: ASPEED reference dev tree properties 
for config")
Signed-off-by: Milton Miller 
Signed-off-by: Eddie James 
---
 drivers/watchdog/aspeed_wdt.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
index ca5b91e..d1987d6 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -232,11 +232,14 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM;
} else {
if (!strcmp(reset_type, "cpu"))
-   wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU;
+   wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU |
+WDT_CTRL_RESET_SYSTEM;
else if (!strcmp(reset_type, "soc"))
-   wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC;
+   wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC |
+WDT_CTRL_RESET_SYSTEM;
else if (!strcmp(reset_type, "system"))
-   wdt->ctrl |= WDT_CTRL_RESET_SYSTEM;
+   wdt->ctrl |= WDT_CTRL_RESET_MODE_FULL_CHIP |
+WDT_CTRL_RESET_SYSTEM;
else if (strcmp(reset_type, "none"))
return -EINVAL;
}
-- 
1.8.3.1



[PATCH 2/2] watchdog: aspeed: Allow configuring for alternate boot

2018-03-09 Thread Eddie James
From: Milton Miller 

Allow the device tree to specify a watchdog to fallover to
the alternate boot source.

The aspeeed watchdog can set a latch directing flash chip select 0 to
chip select 1, allowing boot from an alternate media if the watchdog
is not reset in time.  On the ast2400 bank 1 also goes to flash bank 1,
while on the ast2500 the chip selects are swapped.

Signed-off-by: Milton Miller 
Signed-off-by: Eddie James 
---
 drivers/watchdog/aspeed_wdt.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
index d1987d6..f41d246 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -46,6 +46,7 @@ struct aspeed_wdt_config {
 #define WDT_RELOAD_VALUE   0x04
 #define WDT_RESTART0x08
 #define WDT_CTRL   0x0C
+#define   WDT_CTRL_BOOT_SECONDARY  BIT(7)
 #define   WDT_CTRL_RESET_MODE_SOC  (0x00 << 5)
 #define   WDT_CTRL_RESET_MODE_FULL_CHIP(0x01 << 5)
 #define   WDT_CTRL_RESET_MODE_ARM_CPU  (0x10 << 5)
@@ -245,6 +246,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
}
if (of_property_read_bool(np, "aspeed,external-signal"))
wdt->ctrl |= WDT_CTRL_WDT_EXT;
+   if (of_property_read_bool(np, "aspeed,alt-boot"))
+   wdt->ctrl |= WDT_CTRL_BOOT_SECONDARY;
 
if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE)  {
/*
-- 
1.8.3.1



[PATCH] clk: aspeed: Prevent reset if clock is enabled

2018-03-07 Thread Eddie James
According to the Aspeed specification, the reset and enable sequence
should be done when the clock is stopped. The specification doesn't
define behavior if the reset is done while the clock is enabled.

>From testing on the AST2500, the LPC Controller has problems if the
clock is reset while enabled.

Therefore, check whether the clock is enabled or not before performing
the reset and enable sequence in the Aspeed clock driver.

Signed-off-by: Eddie James 
---
 drivers/clk/clk-aspeed.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 9f7f931..a13054d 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -212,6 +212,12 @@ static int aspeed_clk_enable(struct clk_hw *hw)
u32 clk = BIT(gate->clock_idx);
u32 rst = BIT(gate->reset_idx);
u32 enval;
+   u32 reg;
+
+   /* Only reset/enable/unreset if clock is stopped */
+   regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, ®);
+   if (!(reg & clk))
+   return 0;
 
spin_lock_irqsave(gate->lock, flags);
 
-- 
1.8.3.1



[PATCH] aspeed: watchdog: Add status function

2018-03-26 Thread Eddie James
Populate the status watchdog operation to return the "timeout status"
register of the ASPEED watchdog.

Signed-off-by: Eddie James 
---
 drivers/watchdog/aspeed_wdt.c | 9 +
 1 file changed, 9 insertions(+)

diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
index a5b8eb2..d6dd5c9 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -55,6 +55,7 @@ struct aspeed_wdt_config {
 #define   WDT_CTRL_WDT_INTRBIT(2)
 #define   WDT_CTRL_RESET_SYSTEMBIT(1)
 #define   WDT_CTRL_ENABLE  BIT(0)
+#define WDT_TIMEOUT_STATUS 0x10
 
 /*
  * WDT_RESET_WIDTH controls the characteristics of the external pulse (if
@@ -138,6 +139,13 @@ static int aspeed_wdt_ping(struct watchdog_device *wdd)
return 0;
 }
 
+static unsigned int aspeed_wdt_status(struct watchdog_device *wdd)
+{
+   struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+
+   return readl(wdt->base + WDT_TIMEOUT_STATUS);
+}
+
 static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
  unsigned int timeout)
 {
@@ -171,6 +179,7 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd,
.start  = aspeed_wdt_start,
.stop   = aspeed_wdt_stop,
.ping   = aspeed_wdt_ping,
+   .status = aspeed_wdt_status,
.set_timeout= aspeed_wdt_set_timeout,
.restart= aspeed_wdt_restart,
.owner  = THIS_MODULE,
-- 
1.8.3.1



Re: [PATCH] aspeed: watchdog: Add status function

2018-03-26 Thread Eddie James



On 03/26/2018 05:00 PM, Guenter Roeck wrote:

On 03/26/2018 02:17 PM, Eddie James wrote:

Populate the status watchdog operation to return the "timeout status"
register of the ASPEED watchdog.

Signed-off-by: Eddie James 
---
  drivers/watchdog/aspeed_wdt.c | 9 +
  1 file changed, 9 insertions(+)

diff --git a/drivers/watchdog/aspeed_wdt.c 
b/drivers/watchdog/aspeed_wdt.c

index a5b8eb2..d6dd5c9 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -55,6 +55,7 @@ struct aspeed_wdt_config {
  #define   WDT_CTRL_WDT_INTR    BIT(2)
  #define   WDT_CTRL_RESET_SYSTEM    BIT(1)
  #define   WDT_CTRL_ENABLE    BIT(0)
+#define WDT_TIMEOUT_STATUS    0x10
    /*
   * WDT_RESET_WIDTH controls the characteristics of the external 
pulse (if
@@ -138,6 +139,13 @@ static int aspeed_wdt_ping(struct 
watchdog_device *wdd)

  return 0;
  }
  +static unsigned int aspeed_wdt_status(struct watchdog_device *wdd)
+{
+    struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+
+    return readl(wdt->base + WDT_TIMEOUT_STATUS);
+}


Does the register report WDIOF_* status flags/bits as defined in the 
API ?

This seems more than unlikely.


Ah, I see I'm misusing the API. It does not... I will rework.

Thanks,
Eddie



Guenter


+
  static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
    unsigned int timeout)
  {
@@ -171,6 +179,7 @@ static int aspeed_wdt_restart(struct 
watchdog_device *wdd,

  .start    = aspeed_wdt_start,
  .stop    = aspeed_wdt_stop,
  .ping    = aspeed_wdt_ping,
+    .status    = aspeed_wdt_status,
  .set_timeout    = aspeed_wdt_set_timeout,
  .restart    = aspeed_wdt_restart,
  .owner    = THIS_MODULE,







[PATCH v2 1/2] clk: aspeed: Fix is_enabled for certain clocks

2018-03-08 Thread Eddie James
Some of the Aspeed clocks are disabled by setting the relevant bit in
the "clock stop control" register to one, while others are disabled by
setting their bit to zero. The driver already uses a flag per gate  to
identify this behavior, but doesn't apply it in the clock is_enabled
function.

Use the existing gate flag to correctly return whether or not a clock
is enabled in the aspeed_clk_is_enabled function.

Signed-off-by: Eddie James 
---
 drivers/clk/clk-aspeed.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 9f7f931..1687771 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -259,11 +259,12 @@ static int aspeed_clk_is_enabled(struct clk_hw *hw)
 {
struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
u32 clk = BIT(gate->clock_idx);
+   u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
u32 reg;
 
regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, ®);
 
-   return (reg & clk) ? 0 : 1;
+   return ((reg & clk) == enval) ? 1 : 0;
 }
 
 static const struct clk_ops aspeed_clk_gate_ops = {
-- 
1.8.3.1



[PATCH v2 2/2] clk: aspeed: Prevent reset if clock is enabled

2018-03-08 Thread Eddie James
According to the Aspeed specification, the reset and enable sequence
should be done when the clock is stopped. The specification doesn't
define behavior if the reset is done while the clock is enabled.

>From testing on the AST2500, the LPC Controller has problems if the
clock is reset while enabled.

Therefore, check whether the clock is enabled or not before performing
the reset and enable sequence in the Aspeed clock driver.

Root-caused-by: Lei Yu 
Signed-off-by: Eddie James 
---
 drivers/clk/clk-aspeed.c | 29 +
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 1687771..5eb50c3 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -205,6 +205,18 @@ struct aspeed_clk_soc_data {
.calc_pll = aspeed_ast2400_calc_pll,
 };
 
+static int aspeed_clk_is_enabled(struct clk_hw *hw)
+{
+   struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
+   u32 clk = BIT(gate->clock_idx);
+   u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
+   u32 reg;
+
+   regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, ®);
+
+   return ((reg & clk) == enval) ? 1 : 0;
+}
+
 static int aspeed_clk_enable(struct clk_hw *hw)
 {
struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
@@ -215,6 +227,11 @@ static int aspeed_clk_enable(struct clk_hw *hw)
 
spin_lock_irqsave(gate->lock, flags);
 
+   if (aspeed_clk_is_enabled(hw)) {
+   spin_unlock_irqrestore(gate->lock, flags);
+   return 0;
+   }
+
if (gate->reset_idx >= 0) {
/* Put IP in reset */
regmap_update_bits(gate->map, ASPEED_RESET_CTRL, rst, rst);
@@ -255,18 +272,6 @@ static void aspeed_clk_disable(struct clk_hw *hw)
spin_unlock_irqrestore(gate->lock, flags);
 }
 
-static int aspeed_clk_is_enabled(struct clk_hw *hw)
-{
-   struct aspeed_clk_gate *gate = to_aspeed_clk_gate(hw);
-   u32 clk = BIT(gate->clock_idx);
-   u32 enval = (gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : clk;
-   u32 reg;
-
-   regmap_read(gate->map, ASPEED_CLK_STOP_CTRL, ®);
-
-   return ((reg & clk) == enval) ? 1 : 0;
-}
-
 static const struct clk_ops aspeed_clk_gate_ops = {
.enable = aspeed_clk_enable,
.disable = aspeed_clk_disable,
-- 
1.8.3.1



[PATCH v2 0/2] clk: aspeed: Fix is_enabled and prevent reset if clock enabled

2018-03-08 Thread Eddie James
Here are two fixes for the Aspeed clock driver. The first fixes the is_enabled
clock function to account for different clock gates getting disabled with
either 0s or 1s. The second patch addresses some issue found with the LPC
controller clock if it gets reset while the clock is enabled, which it is by
default. Thanks to Lei Yu for tracking down the LPC issue.

Changes since v1:
 - Fix is_enabled.
 - Check for enabled in the spinlock in the enable function.
 - Use is_enabled instead of more code to read the register.

Eddie James (2):
  clk: aspeed: Fix is_enabled for certain clocks
  clk: aspeed: Prevent reset if clock is enabled

 drivers/clk/clk-aspeed.c | 28 +---
 1 file changed, 17 insertions(+), 11 deletions(-)

-- 
1.8.3.1



Re: [PATCH 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-13 Thread Eddie James



On 03/10/2018 10:50 AM, Guenter Roeck wrote:

On 03/09/2018 11:19 AM, Eddie James wrote:

From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
  drivers/hwmon/pmbus/ucd9000.c | 172 
+-

  1 file changed, 171 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c 
b/drivers/hwmon/pmbus/ucd9000.c

index e3a507f..297da0e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  +#include 
  #include 
  #include 
  #include 
@@ -36,6 +37,7 @@
  #define UCD9000_NUM_PAGES    0xd6
  #define UCD9000_FAN_CONFIG_INDEX    0xe7
  #define UCD9000_FAN_CONFIG    0xe8
+#define UCD9000_MFR_STATUS    0xf3
  #define UCD9000_GPIO_SELECT    0xfa
  #define UCD9000_GPIO_CONFIG    0xfb
  #define UCD9000_DEVICE_ID    0xfd
@@ -63,13 +65,22 @@
  #define UCD901XX_NUM_GPIOS    26
  #define UCD90910_NUM_GPIOS    26
  +#define UCD9000_DEBUGFS_NAME_LEN    24
+#define UCD9000_GPI_COUNT    8
+
  struct ucd9000_data {
  u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
  struct pmbus_driver_info info;
  struct gpio_chip gpio;
+    struct dentry *debugfs;
  };
  #define to_ucd9000_data(_info) container_of(_info, struct 
ucd9000_data, info)

  +struct ucd9000_debugfs_entry {
+    struct i2c_client *client;
+    u8 index;
+};
+
  static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
  {
  int fan_config = 0;
@@ -328,6 +339,156 @@ static int ucd9000_gpio_direction_output(struct 
gpio_chip *gc,

    val);
  }
  +#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 
*buffer)

+{
+    int ret = pmbus_set_page(client, 0);
+
+    if (ret < 0)
+    return ret;
+
+    /*
+ * With the ucd90120 and ucd90124 devices, this command 
[MFR_STATUS]
+ * is 2 bytes long (bits 0-15).  With the ucd90240 this command 
is 5

+ * bytes long.  With all other devices, it is 4 bytes long.
+ */
+    return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, 
buffer);

+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+    struct ucd9000_debugfs_entry *entry = data;
+    struct i2c_client *client = entry->client;
+    u8 buffer[4];
+    int ret;
+
+    /*
+ * This attribute is only created for devices that return 4 
bytes for

+ * status_mfr, so it's safe to call with 4-byte buffer.
+ */
+    ret = ucd9000_get_mfr_status(client, buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    /*
+ * Attribute only created for devices with gpi fault bits at bits
+ * 16-23, which is the second byte of the response.
+ */
+    *val = !!(buffer[1] & BIT(entry->index));
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static int ucd9000_debugfs_show_mfr_status_word2(void *data, u64 *val)
+{
+    struct i2c_client *client = data;
+    __be16 buffer;
+    int ret;
+
+    ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    *val = be16_to_cpu(buffer);
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word2,
+ ucd9000_debugfs_show_mfr_status_word2, NULL,
+ "%04llx\n");
+
+static int ucd9000_debugfs_show_mfr_status_word4(void *data, u64 *val)
+{
+    struct i2c_client *client = data;
+    __be32 buffer;
+    int ret;
+
+    ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    *val = be32_to_cpu(buffer);
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word4,
+ ucd9000_debugfs_show_mfr_status_word4, NULL,
+ "%08llx\n");
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+    const struct i2c_device_id *mid,
+    struct ucd9000_data *data)
+{
+    struct dentry *debugfs;
+    struct ucd9000_debugfs_entry *entries;
+    int i;
+    char name[UCD9000_DEBUGFS_NAME_LEN];
+
+    debugfs = pmbus_get_debugfs_dir(client);
+    if (!debugfs)
+    return -ENOENT;
+
+    data->debugfs = debugfs_create_dir(client->name, debugfs);
+    if

Re: [PATCH 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-13 Thread Eddie James



On 03/13/2018 02:29 PM, Guenter Roeck wrote:

On Tue, Mar 13, 2018 at 02:01:51PM -0500, Eddie James wrote:


On 03/10/2018 10:50 AM, Guenter Roeck wrote:

On 03/09/2018 11:19 AM, Eddie James wrote:

From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
   drivers/hwmon/pmbus/ucd9000.c | 172
+-
   1 file changed, 171 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c
b/drivers/hwmon/pmbus/ucd9000.c
index e3a507f..297da0e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
    * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    */
   +#include 
   #include 
   #include 
   #include 
@@ -36,6 +37,7 @@
   #define UCD9000_NUM_PAGES    0xd6
   #define UCD9000_FAN_CONFIG_INDEX    0xe7
   #define UCD9000_FAN_CONFIG    0xe8
+#define UCD9000_MFR_STATUS    0xf3
   #define UCD9000_GPIO_SELECT    0xfa
   #define UCD9000_GPIO_CONFIG    0xfb
   #define UCD9000_DEVICE_ID    0xfd
@@ -63,13 +65,22 @@
   #define UCD901XX_NUM_GPIOS    26
   #define UCD90910_NUM_GPIOS    26
   +#define UCD9000_DEBUGFS_NAME_LEN    24
+#define UCD9000_GPI_COUNT    8
+
   struct ucd9000_data {
   u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
   struct pmbus_driver_info info;
   struct gpio_chip gpio;
+    struct dentry *debugfs;
   };
   #define to_ucd9000_data(_info) container_of(_info, struct
ucd9000_data, info)
   +struct ucd9000_debugfs_entry {
+    struct i2c_client *client;
+    u8 index;
+};
+
   static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
   {
   int fan_config = 0;
@@ -328,6 +339,156 @@ static int ucd9000_gpio_direction_output(struct
gpio_chip *gc,
     val);
   }
   +#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8
*buffer)
+{
+    int ret = pmbus_set_page(client, 0);
+
+    if (ret < 0)
+    return ret;
+
+    /*
+ * With the ucd90120 and ucd90124 devices, this command
[MFR_STATUS]
+ * is 2 bytes long (bits 0-15).  With the ucd90240 this command is
5
+ * bytes long.  With all other devices, it is 4 bytes long.
+ */
+    return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS,
buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+    struct ucd9000_debugfs_entry *entry = data;
+    struct i2c_client *client = entry->client;
+    u8 buffer[4];
+    int ret;
+
+    /*
+ * This attribute is only created for devices that return 4 bytes
for
+ * status_mfr, so it's safe to call with 4-byte buffer.
+ */
+    ret = ucd9000_get_mfr_status(client, buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    /*
+ * Attribute only created for devices with gpi fault bits at bits
+ * 16-23, which is the second byte of the response.
+ */
+    *val = !!(buffer[1] & BIT(entry->index));
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static int ucd9000_debugfs_show_mfr_status_word2(void *data, u64 *val)
+{
+    struct i2c_client *client = data;
+    __be16 buffer;
+    int ret;
+
+    ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    *val = be16_to_cpu(buffer);
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word2,
+ ucd9000_debugfs_show_mfr_status_word2, NULL,
+ "%04llx\n");
+
+static int ucd9000_debugfs_show_mfr_status_word4(void *data, u64 *val)
+{
+    struct i2c_client *client = data;
+    __be32 buffer;
+    int ret;
+
+    ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    *val = be32_to_cpu(buffer);
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word4,
+ ucd9000_debugfs_show_mfr_status_word4, NULL,
+ "%08llx\n");
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+    const struct i2c_device_id *mid,
+    struct ucd9000_data *data)
+{
+    struct dentry *debugfs;
+    struct ucd9000_debugfs_entry *entries;
+    int i;
+    char name[UCD9000_DEBUGFS_NAME_LEN];
+
+    debugfs = pmbus_get_debugfs_dir(client);
+

[PATCH v2 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-13 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 140 +-
 1 file changed, 139 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index 023fb9e..b073d8e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -36,6 +37,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -63,13 +65,22 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -306,6 +317,124 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
 }
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   /*
+* With the ucd90120 and ucd90124 devices, this command [MFR_STATUS]
+* is 2 bytes long (bits 0-15).  With the ucd90240 this command is 5
+* bytes long.  With all other devices, it is 4 bytes long.
+*/
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[4];
+   int ret;
+
+   /*
+* This attribute is only created for devices that return 4 bytes for
+* status_mfr, so it's safe to call with 4-byte buffer.
+*/
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[5] = { 0 };   /* Need max 5 bytes for any ucd9000 chip. */
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   return simple_read_from_buffer(buf, count, ppos, buffer, ret);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+   return -ENOENT;
+
+   /*
+* Of the chips this driver supports, only the UCD9090, UCD90160,
+* and UCD90910 report GPI faults in their MFR_STATUS register, so only
+* create the GPI fault debugfs attributes for those chips.
+*/
+   if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+   mid->driver_data == ucd90910

[PATCH v2 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-13 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Changes since v1:
 - dropped dev_err messages
 - made gpio chip registration conditional on having gpio pins
 - made mfr_status debugfs attribute more simple

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 341 +-
 1 file changed, 340 insertions(+), 1 deletion(-)

-- 
1.8.3.1



[PATCH v2 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-13 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 201 ++
 1 file changed, 201 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..023fb9e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +36,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +58,15 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+   struct gpio_chip gpio;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +166,146 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret, config, out_val;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   if (direction_out) {
+   out_val = requested_out ? UCD9

Re: [PATCH] fsi: occ: fix a NULL vs IS_ERR() check

2018-11-26 Thread Eddie James




On 11/26/2018 02:11 AM, Dan Carpenter wrote:

The platform_device_register_full() function doesn't return NULL, it
returns error pointers.

Fixes: 4e01f5644463 ("fsi: Add On-Chip Controller (OCC) driver")
Signed-off-by: Dan Carpenter 


Thanks.

Reviewed-by: Eddie James 


---
  drivers/fsi/fsi-occ.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index a2301cea1cbb..d8695f67622f 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -546,7 +546,7 @@ static int occ_probe(struct platform_device *pdev)

hwmon_dev_info.id = occ->idx;
hwmon_dev = platform_device_register_full(&hwmon_dev_info);
-   if (!hwmon_dev)
+   if (IS_ERR(hwmon_dev))
dev_warn(dev, "failed to create hwmon device\n");

return 0;




Re: [PATCH v5 0/2] media: platform: Add Aspeed Video Engine Driver

2018-11-27 Thread Eddie James




On 11/15/2018 03:20 AM, Hans Verkuil wrote:

On 11/12/2018 10:00 PM, Eddie James wrote:

From: Eddie James 

The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting as a service processor, the Video Engine can
capture the host processor graphics output.

This series adds a V4L2 driver for the VE, providing the usual V4L2 streaming
interface by way of videobuf2. Each frame, the driver triggers the hardware to
capture the host graphics output and compress it to JPEG format.

I reviewed the driver, and there is still confusion about active timings and
detected timings. You are really close, but it is still not right.

The timings returned by G_DV_TIMINGS should never change, unless by calling
S_DV_TIMINGS. The format returned by G_FMT should never change, unless by
calling S_DV_TIMINGS (which implicitly changes the format) or S_FMT.

Timings changes from the video or calling QUERY_DV_TIMINGS should have NO
effect on the timings/format returned by G_DV_TIMINGS/G_FMT.

Obviously, if the video timings change, then streaming will halt and an event
is issued so userspace can take action.


Thanks, I have made some changes for v6.




v4l-utils HEAD dd3ff81f58c4e1e6f33765dc61ad33c48ae6bb07

v4l2-compliance SHA: not available, 32 bits




Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
fail: v4l2-test-controls.cpp(823): failed to find event for 
control 'Chroma Subsampling'

That's weird. It's not clear to me why this fails.


test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: FAIL
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 3 Private Controls: 0

Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)

Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
fail: v4l2-test-buffers.cpp(422): dmabuf_valid ^ !!(caps & 
V4L2_BUF_CAP_SUPPORTS_DMABUF)

I updated v4l2-compliance, as this test was a bit too strict.


test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: FAIL
test VIDIOC_EXPBUF: OK (Not Supported)

Test input 0:

Streaming ioctls:
test read/write: OK
test blocking wait: OK
test MMAP: OK
test USERPTR: OK (Not Supported)
fail: v4l2-test-buffers.cpp(1102): exp_q.reqbufs(expbuf_node, 
q.g_buffers())
fail: v4l2-test-buffers.cpp(1192): setupDmaBuf(expbuf_node, 
node, q, exp_q)

You need to add:

.vidioc_expbuf = vb2_ioctl_expbuf,

to aspeed_video_ioctl_ops.


test DMABUF: FAIL

Total: 48, Succeeded: 45, Failed: 3, Warnings: 0

The apparent rate of change to the v4l2/vb2 API makes it difficult to pass
these tests. None of the failing tests even ran last time I submitted my
series. And V4L2_BUF_CAP_SUPPORTS_DMABUF is undefined in 4.19.

When developing new drivers you should use the master branch of
https://git.linuxtv.org/media_tree.git/


Our 4.18 tree has quite a few chip-specific patches that I need that are 
not upstreamed yet, so it's not possible for me to use that.


Thanks,
Eddie



The v4l-utils repo is kept in sync with the latest code from that master branch.

Regards,

Hans


Changes since v4:
  - Set real min and max resolution in enum_dv_timings
  - Add check for vb2_is_busy before settings the timings
  - Set max frame rate to the actual max rather than max + 1
  - Correct the input status to 0.
  - Rework resolution change to only set the relevant h/w regs during startup or
when set_timings is called.

Changes since v3:
  - Switch update reg function to use u32 clear rather than unsigned long mask
  - Add timing information from host VGA signal
  - Fix binding documentation mispelling
  - Fix upper case hex values
  - Add wait_prepare and wait_finish
  - Set buffer state to queued (rather than error) if streaming fails to start
  - Switch engine busy print statement to error
  - Removed a couple unecessary type assignments in v4l2 ioctls
  - Added query_dv_timings, fixed get_dv_timings
  - Corrected source change event to fire if and only if size actually changes
  - Locked open and release

Changes since v2:
  - Switch to streaming interface. This involved a lot of changes.
  - Rework memory allocation due to using vid

Re: [PATCH v11 5/8] i2c: fsi: Add transfer implementation

2018-07-10 Thread Eddie James




On 07/09/2018 05:41 PM, Wolfram Sang wrote:

+   cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr >> 1);

I just noticed this and wonder: Don't you need the LSB of the address?
It is not the RW flag, this is encoded in msg->flags.


So, the hardware interprets the LSB as the RW flag. It wouldn't be 
possible to have a device addressed with the LSB set on this I2C master.




Also, no seperate handling for 10 bit addresses? Technically, 7-bit 0x50
is different on the wire from 10-bit 0x050. This is minor, though. There
are no 10-bit devices out there. Still, did you test 10-bit support?


Indeed, real 10-bit addresses require some additional manipulation of 
this I2C master in order to work. We don't support it right now.


Thanks,
Eddie



Rest looks good.





Re: [PATCH v11 5/8] i2c: fsi: Add transfer implementation

2018-07-10 Thread Eddie James




On 07/10/2018 01:50 PM, Wolfram Sang wrote:

+   cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr >> 1);

I just noticed this and wonder: Don't you need the LSB of the address?
It is not the RW flag, this is encoded in msg->flags.

So, the hardware interprets the LSB as the RW flag. It wouldn't be possible
to have a device addressed with the LSB set on this I2C master.

But msg->addr is 7 bit and LSB aligned. If I am not horribly wrong, with
the above code, an EEPROM at 0x50 would show up as 0x28 with your
driver?


Sorry, what do you mean "show up as"? Yes, we could first shift all our 
addresses in user-space before passing them to the driver, so that the 
msg->addr field is exactly what the hardware expects already... This 
would be non-trivial for our users considering all our documentation 
represents the addresses as the top 7 bits of a byte :(





Indeed, real 10-bit addresses require some additional manipulation of this
I2C master in order to work. We don't support it right now.

Then you should remove the associated FUNC flag.


Ah, but due to the addressing situation, tools like i2cget don't work 
with our addresses unless the 10 bit flag is specified. For example, we 
may want to access 0xA0.








Re: [PATCH v11 5/8] i2c: fsi: Add transfer implementation

2018-07-10 Thread Eddie James




On 07/10/2018 02:39 PM, Wolfram Sang wrote:

Sorry, what do you mean "show up as"? Yes, we could first shift all our
addresses in user-space before passing them to the driver, so that the
msg->addr field is exactly what the hardware expects already... This would
be non-trivial for our users considering all our documentation represents
the addresses as the top 7 bits of a byte :(

Ah, now I understand the whole situation! Good that I asked. But I have
bad news for you:

msg->addr is 7 bit and LSB aligned. No way around that. This is how
Linux I2C worked since the beginning. You have to adapt to it.

I know what you mean. Most doumentation I get has the addresses in 8
bit, i.e. 7 bit address shifted + RW bit. But sorry again, the Linux
representation is different and all drivers have to adhere to that.

An EEPROM ist at 0x50 in Linux. There is no write addr 0xa0 and read
addr 0xa1.


OK, I understand! Will test and resend with conforming addressing. 
Thanks for all the feedback!


Eddie




Indeed, real 10-bit addresses require some additional manipulation of this
I2C master in order to work. We don't support it right now.

Then you should remove the associated FUNC flag.

Ah, but due to the addressing situation, tools like i2cget don't work with
our addresses unless the 10 bit flag is specified. For example, we may want
to access 0xA0.

This is a kinda dirty workaround to the above problem. It is even wrong
because 10-bit addresses look totally different on the wire.

Sorry for the hazzle with the docs, but there is no way around that.





Re: [PATCH v10 5/7] i2c: fsi: Add transfer implementation

2018-06-27 Thread Eddie James




On 06/25/2018 09:38 PM, Wolfram Sang wrote:

On Wed, Jun 13, 2018 at 02:36:17PM -0500, Eddie James wrote:

Execute I2C transfers from the FSI-attached I2C master. Use polling
instead of interrupts as we have no hardware IRQ over FSI.

Signed-off-by: Eddie James 
---
  drivers/i2c/busses/i2c-fsi.c | 195 ++-
  1 file changed, 193 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 4611a0b..8c0a6cb 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -155,6 +155,7 @@ struct fsi_i2c_port {
struct i2c_adapter  adapter;
struct fsi_i2c_master   *master;
u16 port;
+   u16 xfrd;
  };
  
  static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,

@@ -230,6 +231,99 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
  }
  
+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,

+bool stop)
+{
+   struct fsi_i2c_master *i2c = port->master;
+   u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+
+   port->xfrd = 0;
+
+   if (msg->flags & I2C_M_RD)
+   cmd |= I2C_CMD_READ;

Since you support MANGLING, I'd think you can easily support
I2C_M_REV_DIR_ADDR here, too?


Hm, I don't really understand the purpose of that flag. From the docs:

This toggles the Rd/Wr flag. That is, if you want to do a write, but
    need to emit an Rd instead of a Wr, or vice versa, you set this
    flag. For example:
S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P

I don't think our hardware supports this type of operation.

Thanks,
Eddie







Re: [PATCH v10 7/7] i2c: fsi: Add bus recovery

2018-06-27 Thread Eddie James




On 06/25/2018 09:38 PM, Wolfram Sang wrote:

On Wed, Jun 13, 2018 at 02:36:19PM -0500, Eddie James wrote:

Bus recovery should reset the engine and force clock the bus 9 times
to recover most situations.

Signed-off-by: Eddie James 
---
  drivers/i2c/busses/i2c-fsi.c | 19 +++
  1 file changed, 19 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index d6cab4b..940b198 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -611,6 +611,24 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_BLOCK_DATA;
  }
  
+static int fsi_i2c_recover_bus(struct i2c_adapter *adap)

+{
+   int rc;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *master = port->master;
+
+   mutex_lock(&master->lock);
+
+   rc = fsi_i2c_reset(master, port->port);
+
+   mutex_unlock(&master->lock);
+   return rc;
+}
+
+static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
+   .recover_bus = fsi_i2c_recover_bus,
+};
+

This all won't have any effect since you never call i2c_recover_bus
which calls back into i2c_bus_recovery_info callbacks.


Ah, I thought there would be some use of this in the core or in client 
drivers, or some ioctl interface. Would there be any outside users of 
these callbacks in the future?





Re: [PATCH v10 4/7] i2c: fsi: Add abort and hardware reset procedures

2018-06-27 Thread Eddie James




On 06/25/2018 09:38 PM, Wolfram Sang wrote:

On Wed, Jun 13, 2018 at 02:36:16PM -0500, Eddie James wrote:

Add abort procedure for failed transfers. Add engine and bus reset
procedures to recover from as many faults as possible.

I think this is a way too aggressive recovery. Your are doing the 9
pulse toggles basically on any error while this is only when the device
keeps SDA low and you want to recover from that. If SDA is not stuck
low, sending a STOP should do. Or do you have a known case where this is
not going to work?


It is aggressive, but I don't see the harm in doing this on every error. 
There are some other error conditions with this hardware which may 
require the clock toggling, such as "bus arbitration lost." I think this 
is the safest option for this hardware, and this routine has been tested 
for many years.




Also, you implement the pulse toggling manually. Can't you just populate
{get|set}_{scl|sda} and use the generic routine we have in the core?


I see that the generic implementation breaks the loop if it sees the 
clock isn't high after setting it, or if SDA goes high. I think it's 
safer to finish the reset for our hardware. Plus, we actually have 
different registers for setting 0 or 1 to the clock/data, so we save 
some cpu cycles by doing it directly instead of implementing set_scl/sda 
and having to check val every time :)


If you feel very strongly that this recovery procedure needs to be 
reduced, then I will work on that and have to do some extensive testing.


Thanks!
Eddie







Re: [PATCH v10 0/7] i2c: Add FSI-attached I2C master algorithm

2018-06-27 Thread Eddie James




On 06/25/2018 09:39 PM, Wolfram Sang wrote:

On Wed, Jun 13, 2018 at 02:36:12PM -0500, Eddie James wrote:

This series adds an algorithm for an I2C master physically located on an FSI
slave device. The I2C master has multiple ports, each of which may be connected
to an I2C slave. Access to the I2C master registers is achieved over FSI bus.

Due to the multi-port nature of the I2C master, the driver instantiates a new
I2C adapter for each port connected to a slave. The connected ports should be
defined in the device tree under the I2C master device.

Thanks for this series and your patience.

While I can see why it also helps reviewing to send it as a series of
multiple patches, I consider applying the driver itself as just one
hunk. I am not decided on this yet.

I have a few comments, especially about recovery. I replied to the
relevant patches with more detail.

Also, are you (or someone from your company) willing to maintain the
driver? Then, an addition to MAINTAINERS would be much appreciated.


Thanks for the review Wolfram! I addressed your comments about recovery, 
please let me know what you think. I will fix the email for the dt patch 
and make an addition to MAINTAINERS for the next version.


Thanks,
Eddie



Thanks,

Wolfram





Re: [PATCH v7 2/7] drivers/i2c: Add FSI-attached I2C master algorithm

2018-05-30 Thread Eddie James




On 05/29/2018 06:42 PM, Andy Shevchenko wrote:

On Wed, May 30, 2018 at 1:24 AM, Eddie James  wrote:

From: "Edward A. James" 

Add register definitions for FSI-attached I2C master and functions to
access those registers over FSI. Add an FSI driver so that our I2C bus
is probed up during an FSI scan.

This looks like reinventing a wheel in some ways.

See my comments below.


+/*
+ * Copyright 2017 IBM Corporation
+ *
+ * Eddie James 
+ *
+ * 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.
+ */

We are using SPDX identifiers. Can you?


Sure.




+/* Find left shift from first set bit in m */
+#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1ULL)

Oh. What about GENMASK()?


+/* Extract field m from v */
+#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m))
+
+/* Set field m of v to val */
+#define SETFIELD(m, v, val)\
+   (((v) & ~(m)) | typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))

Oh, what about 
https://elixir.bootlin.com/linux/latest/source/include/linux/bitfield.h
?


Good idea, thanks.




+#define I2C_CMD_WITH_START 0x8000
+#define I2C_CMD_WITH_ADDR  0x4000
+#define I2C_CMD_RD_CONT0x2000
+#define I2C_CMD_WITH_STOP  0x1000
+#define I2C_CMD_FORCELAUNCH0x0800

BIT() ?


+#define I2C_CMD_ADDR   0x00fe
+#define I2C_CMD_READ   0x0001

GENMASK()? Though precisely here it might be good to leave explicit values.


+#define I2C_CMD_LEN0x
+#define I2C_MODE_CLKDIV0x
+#define I2C_MODE_PORT  0xfc00
+#define I2C_MODE_ENHANCED  0x0008
+#define I2C_MODE_DIAG  0x0004
+#define I2C_MODE_PACE_ALLOW0x0002
+#define I2C_MODE_WRAP  0x0001

What are they? Masks? Bit fields? Just plain numbers?


+#define I2C_WATERMARK_HI   0xf000
+#define I2C_WATERMARK_LO   0x00f0

GENMASK() ?


+#define I2C_INT_INV_CMD0x8000
+#define I2C_INT_PARITY 0x4000
+#define I2C_INT_BE_OVERRUN 0x2000
+#define I2C_INT_BE_ACCESS  0x1000
+#define I2C_INT_LOST_ARB   0x0800
+#define I2C_INT_NACK   0x0400
+#define I2C_INT_DAT_REQ0x0200
+#define I2C_INT_CMD_COMP   0x0100
+#define I2C_INT_STOP_ERR   0x0080
+#define I2C_INT_BUSY   0x0040
+#define I2C_INT_IDLE   0x0020

BIT()


+#define I2C_INT_ENABLE 0xff80
+#define I2C_INT_ERR0xfcc0
+#define I2C_STAT_INV_CMD   0x8000
+#define I2C_STAT_PARITY0x4000
+#define I2C_STAT_BE_OVERRUN0x2000
+#define I2C_STAT_BE_ACCESS 0x1000
+#define I2C_STAT_LOST_ARB  0x0800
+#define I2C_STAT_NACK  0x0400
+#define I2C_STAT_DAT_REQ   0x0200
+#define I2C_STAT_CMD_COMP  0x0100
+#define I2C_STAT_STOP_ERR  0x0080
+#define I2C_STAT_MAX_PORT  0x000f
+#define I2C_STAT_ANY_INT   0x8000
+#define I2C_STAT_SCL_IN0x0800
+#define I2C_STAT_SDA_IN0x0400
+#define I2C_STAT_PORT_BUSY 0x0200
+#define I2C_STAT_SELF_BUSY 0x0100

BIT()


+#define I2C_STAT_FIFO_COUNT0x00ff

GENMASK()


+
+#define I2C_STAT_ERR   0xfc80
+#define I2C_STAT_ANY_RESP  0xff80
+#define I2C_ESTAT_FIFO_SZ  0xff00

GENMASK()


+#define I2C_ESTAT_SCL_IN_SY0x8000
+#define I2C_ESTAT_SDA_IN_SY0x4000
+#define I2C_ESTAT_S_SCL0x2000
+#define I2C_ESTAT_S_SDA0x1000
+#define I2C_ESTAT_M_SCL0x0800
+#define I2C_ESTAT_M_SDA0x0400
+#define I2C_ESTAT_HI_WATER 0x0200
+#define I2C_ESTAT_LO_WATER 0x0100
+#define I2C_ESTAT_PORT_BUSY0x0080
+#define I2C_ESTAT_SELF_BUSY0x0040

BIT()


+#define I2C_ESTAT_VERSION  0x001f

GENMASK()


+   __be32 data_be;

No need to have a suffix. If anything can go wrong we have a tool,
it's called sparse. It will catch out inappropriate use of __bitwise
types.


I already have a variable called data...




+   __be32 data_be = cpu_to_be32(*data);

cpu_to_be32p()  IIUC?


Sure.




+static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
+{
+   int rc;
+   u32 mode = I2C_MODE_ENHANCED, extended_status, watermark = 0;
+   u32 interrupt = 0;

Redundant assignment.


No, I need to set the interrupt register to 0, so I must set this.




+
+   /* since we use polling, disable interrupts */
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
+   if (rc)
+   return rc;
+   return rc;

Would be non-zero?


No, fsi_i2c_write_reg returns non-zero on error, zero on success. That 
is what I want.


Thanks,
Eddie




+}




Re: [PATCH v7 3/7] drivers/i2c: Add port structure to FSI algorithm

2018-05-30 Thread Eddie James




On 05/29/2018 06:19 PM, Andy Shevchenko wrote:

On Wed, May 30, 2018 at 1:24 AM, Eddie James  wrote:

From: "Edward A. James" 

Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports for each master are defined in the devicetree.
+#include 



+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+   int rc;
+   struct fsi_device *fsi = port->master->fsi;
+   u32 mode, dummy = 0;
+   u16 old_port;
+
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   old_port = GETFIELD(I2C_MODE_PORT, mode);
+
+   if (old_port != port->port) {

Why not simple

if (port->port == GETFIELD())
   return 0;

?


Sure.




+   /* reset engine when port is changed */
+   rc = fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+   if (rc)
+   return rc;
+   }
+   return rc;

It's hardly would be non-zero, right?


No, fsi_i2c_read_reg and fsi_i2c_write_reg both return 0 on success and 
non-zero on error. That is the desired behavior of this function also.





+}
  static int fsi_i2c_probe(struct device *dev)
  {

Isn't below somehow repeats of_i2c_register_devices() ?
Why not to use it?


Because I need to assign all these port structure fields. Also looks 
like of_i2c_register_devices creates new devices; I just want an adapter 
for each port.





+   /* Add adapter for each i2c port of the master. */
+   for_each_available_child_of_node(dev->of_node, np) {
+   rc = of_property_read_u32(np, "reg", &port_no);
+   if (rc || port_no > USHRT_MAX)
+   continue;
+
+   port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+   if (!port)
+   break;
+
+   port->master = i2c;
+   port->port = port_no;
+
+   port->adapter.owner = THIS_MODULE;
+   port->adapter.dev.of_node = np;
+   port->adapter.dev.parent = dev;
+   port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.algo_data = port;
+
+   snprintf(port->adapter.name, sizeof(port->adapter.name),
+"i2c_bus-%u", port_no);
+
+   rc = i2c_add_adapter(&port->adapter);
+   if (rc < 0) {
+   dev_err(dev, "Failed to register adapter: %d\n", rc);
+   devm_kfree(dev, port);

This hurts my eyes. Why?!


What would you suggest instead?




+   continue;
+   }
+
+   list_add(&port->list, &i2c->ports);
+   }
+
 dev_set_drvdata(dev, i2c);

 return 0;
  }
+   if (!list_empty(&i2c->ports)) {

My gosh, this is done already in list_for_each*()


No, list_for_each_entry does NOT check if the list is empty or if the 
first entry is NULL.


Thanks,
Eddie




+   list_for_each_entry(port, &i2c->ports, list) {
+   i2c_del_adapter(&port->adapter);
+   }
+   }







Re: [PATCH v7 3/7] drivers/i2c: Add port structure to FSI algorithm

2018-05-30 Thread Eddie James




On 05/29/2018 06:19 PM, Andy Shevchenko wrote:

On Wed, May 30, 2018 at 1:24 AM, Eddie James  wrote:

From: "Edward A. James" 

Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports for each master are defined in the devicetree.
+#include 



+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+   int rc;
+   struct fsi_device *fsi = port->master->fsi;
+   u32 mode, dummy = 0;
+   u16 old_port;
+
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   old_port = GETFIELD(I2C_MODE_PORT, mode);
+
+   if (old_port != port->port) {

Why not simple

if (port->port == GETFIELD())
   return 0;

?


+   /* reset engine when port is changed */
+   rc = fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+   if (rc)
+   return rc;
+   }
+   return rc;

It's hardly would be non-zero, right?


Sorry, misunderstood your comment here. You are correct, it can only be 
zero.


Thanks,
Eddie




+}
  static int fsi_i2c_probe(struct device *dev)
  {

Isn't below somehow repeats of_i2c_register_devices() ?
Why not to use it?


+   /* Add adapter for each i2c port of the master. */
+   for_each_available_child_of_node(dev->of_node, np) {
+   rc = of_property_read_u32(np, "reg", &port_no);
+   if (rc || port_no > USHRT_MAX)
+   continue;
+
+   port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+   if (!port)
+   break;
+
+   port->master = i2c;
+   port->port = port_no;
+
+   port->adapter.owner = THIS_MODULE;
+   port->adapter.dev.of_node = np;
+   port->adapter.dev.parent = dev;
+   port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.algo_data = port;
+
+   snprintf(port->adapter.name, sizeof(port->adapter.name),
+"i2c_bus-%u", port_no);
+
+   rc = i2c_add_adapter(&port->adapter);
+   if (rc < 0) {
+   dev_err(dev, "Failed to register adapter: %d\n", rc);
+   devm_kfree(dev, port);

This hurts my eyes. Why?!


+   continue;
+   }
+
+   list_add(&port->list, &i2c->ports);
+   }
+
 dev_set_drvdata(dev, i2c);

 return 0;
  }
+   if (!list_empty(&i2c->ports)) {

My gosh, this is done already in list_for_each*()


+   list_for_each_entry(port, &i2c->ports, list) {
+   i2c_del_adapter(&port->adapter);
+   }
+   }







Re: [PATCH v7 5/7] drivers/i2c: Add transfer implementation for FSI algorithm

2018-05-30 Thread Eddie James




On 05/29/2018 07:08 PM, Andy Shevchenko wrote:

On Wed, May 30, 2018 at 1:24 AM, Eddie James  wrote:

From: "Edward A. James" 

Execute I2C transfers from the FSI-attached I2C master. Use polling
instead of interrupts as we have no hardware IRQ over FSI.
+   if (msg->flags & I2C_M_RD)
+   cmd |= I2C_CMD_READ;

I think we have a helper for this, though not sure.


Didn't see any other I2C drivers using any helper for msg->flags.




+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+   int write;
+   int rc = 0;

Redundant assignment.


+   struct fsi_i2c_master *i2c = port->master;
+   int bytes_to_write = i2c->fifo_size - fifo_count;
+   int bytes_remaining = msg->len - port->xfrd;
+   if (bytes_to_write > bytes_remaining)
+   bytes_to_write = bytes_remaining;

_write = min(_write, _remaining);


+   while (bytes_to_write > 0) {
+   write = bytes_to_write;
+   /* fsi limited to max 4 byte aligned ops */
+   if (bytes_to_write > 4)
+   write = 4;
+   else if (write == 3)
+   write = 2;

write = min_t(int, 4, rounddown_pow_of_two(bytes_to_write));

Also check it carefully, it might be optimized even more, though I
didn't think much.


I think it is more readable this way, and I'm not convinced the 
min(rounddown()) is faster. I did however add a common function to do 
this check since it's performed in both the read and write fifo 
functions. Let me know what you think on v8.


Thanks,
Eddie



[PATCH v8 7/7] i2c: fsi: Add bus recovery

2018-05-30 Thread Eddie James
Bus recovery should reset the engine and force clock the bus 9 times
to recover most situations.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 19 +++
 1 file changed, 19 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 5dad16a..a8b7d89 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -605,6 +605,24 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
| I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
 }
 
+static int fsi_i2c_recover_bus(struct i2c_adapter *adap)
+{
+   int rc;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *master = port->master;
+
+   mutex_lock(&master->lock);
+
+   rc = fsi_i2c_reset(master, port->port);
+
+   mutex_unlock(&master->lock);
+   return rc;
+}
+
+static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
+   .recover_bus = fsi_i2c_recover_bus,
+};
+
 static const struct i2c_algorithm fsi_i2c_algorithm = {
.master_xfer = fsi_i2c_xfer,
.functionality = fsi_i2c_functionality,
@@ -647,6 +665,7 @@ static int fsi_i2c_probe(struct device *dev)
port->adapter.dev.of_node = np;
port->adapter.dev.parent = dev;
port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
port->adapter.algo_data = port;
 
snprintf(port->adapter.name, sizeof(port->adapter.name),
-- 
1.8.3.1



[PATCH v8 6/7] i2c: fsi: Add I2C master locking

2018-05-30 Thread Eddie James
Since there are many ports per master, each with it's own adapter and
chardev, we need some locking to prevent transfers from changing the
master state while other transfers are in progress.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 16 
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index f24e4b9..5dad16a 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #define FSI_ENGID_I2C  0x7
@@ -142,6 +143,7 @@ struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
struct list_headports;
+   struct mutexlock;
 };
 
 struct fsi_i2c_port {
@@ -569,11 +571,14 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs,
int i, rc;
unsigned long start_time;
struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *master = port->master;
struct i2c_msg *msg;
 
+   mutex_lock(&master->lock);
+
rc = fsi_i2c_set_port(port);
if (rc)
-   return rc;
+   goto unlock;
 
for (i = 0; i < num; ++i) {
msg = msgs + i;
@@ -581,15 +586,17 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs,
 
rc = fsi_i2c_start(port, msg, i == num - 1);
if (rc)
-   return rc;
+   goto unlock;
 
rc = fsi_i2c_wait(port, msg,
  adap->timeout - (jiffies - start_time));
if (rc)
-   return rc;
+   goto unlock;
}
 
-   return 0;
+unlock:
+   mutex_unlock(&master->lock);
+   return rc;
 }
 
 static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
@@ -615,6 +622,7 @@ static int fsi_i2c_probe(struct device *dev)
if (!i2c)
return -ENOMEM;
 
+   mutex_init(&i2c->lock);
i2c->fsi = to_fsi_dev(dev);
INIT_LIST_HEAD(&i2c->ports);
 
-- 
1.8.3.1



[PATCH v8 0/7] i2c: Add FSI-attached I2C master algorithm

2018-05-30 Thread Eddie James
This series adds an algorithm for an I2C master physically located on an FSI
slave device. The I2C master has multiple ports, each of which may be connected
to an I2C slave. Access to the I2C master registers is achieved over FSI bus.

Due to the multi-port nature of the I2C master, the driver instantiates a new
I2C adapter for each port connected to a slave. The connected ports should be
defined in the device tree under the I2C master device.

Changes since v7
 - Fix grammer in Kconfig (a -> an)
 - Change I2C registers to use BIT and GENMASK
 - Remove custom macros and use FIELD_PREP and FIELD_GET
 - Fix a few unecessary initializations and "return rc" that are always zero
 - Clean up the read/write fifo functions a bit
 - Few other clean-up items

Changes since v6
 - Remove spinlock for reset functionality; it's unecessary and doesn't work
   with the latest FSI core.
 - Use a mutex instead of a semaphore, and don't wait for timeout to get the
   lock.
 - Use usleeps instead of schedule_timeout; it's not worth the overhead when
   the wait should be very short in between sending the command and receiving
   the response.

Changes since v5
 - Fix reset functionality and do a reset after every transfer failure

Eddie James (7):
  dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation
  i2c: Add FSI-attached I2C master algorithm
  i2c: fsi: Add port structures
  i2c: fsi: Add abort and hardware reset procedures
  i2c: fsi: Add transfer implementation
  i2c: fsi: Add I2C master locking
  i2c: fsi: Add bus recovery

 Documentation/devicetree/bindings/i2c/i2c-fsi.txt |  40 ++
 drivers/i2c/busses/Kconfig|  11 +
 drivers/i2c/busses/Makefile   |   1 +
 drivers/i2c/busses/i2c-fsi.c  | 722 ++
 4 files changed, 774 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

-- 
1.8.3.1



[PATCH v8 2/7] i2c: Add FSI-attached I2C master algorithm

2018-05-30 Thread Eddie James
Add register definitions for FSI-attached I2C master and functions to
access those registers over FSI. Add an FSI driver so that our I2C bus
is probed up during an FSI scan.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/Kconfig   |  11 ++
 drivers/i2c/busses/Makefile  |   1 +
 drivers/i2c/busses/i2c-fsi.c | 234 +++
 3 files changed, 246 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4f8df2e..cddd159 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1330,4 +1330,15 @@ config I2C_ZX2967
  This driver can also be built as a module. If so, the module will be
  called i2c-zx2967.
 
+config I2C_FSI
+   tristate "FSI I2C driver"
+   depends on FSI
+   help
+ Driver for FSI bus attached I2C masters. These are I2C masters that
+ are connected to the system over an FSI bus, instead of the more
+ common PCI or MMIO interface.
+
+ This driver can also be built as a module. If so, the module will be
+ called as i2c-fsi.
+
 endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5a86914..4909fd6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -137,5 +137,6 @@ obj-$(CONFIG_I2C_PCA_ISA)   += i2c-pca-isa.o
 obj-$(CONFIG_I2C_SIBYTE)   += i2c-sibyte.o
 obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
 obj-$(CONFIG_SCx200_ACB)   += scx200_acb.o
+obj-$(CONFIG_I2C_FSI)  += i2c-fsi.o
 
 ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
new file mode 100644
index 000..e1b183c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FSI-attached I2C master algorithm
+ *
+ * Copyright 2018 IBM Corporation
+ *
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define FSI_ENGID_I2C  0x7
+
+#define I2C_DEFAULT_CLK_DIV6
+
+/* i2c registers */
+#define I2C_FSI_FIFO   0x00
+#define I2C_FSI_CMD0x04
+#define I2C_FSI_MODE   0x08
+#define I2C_FSI_WATER_MARK 0x0C
+#define I2C_FSI_INT_MASK   0x10
+#define I2C_FSI_INT_COND   0x14
+#define I2C_FSI_OR_INT_MASK0x14
+#define I2C_FSI_INTS   0x18
+#define I2C_FSI_AND_INT_MASK   0x18
+#define I2C_FSI_STAT   0x1C
+#define I2C_FSI_RESET_I2C  0x1C
+#define I2C_FSI_ESTAT  0x20
+#define I2C_FSI_RESET_ERR  0x20
+#define I2C_FSI_RESID_LEN  0x24
+#define I2C_FSI_SET_SCL0x24
+#define I2C_FSI_PORT_BUSY  0x28
+#define I2C_FSI_RESET_SCL  0x2C
+#define I2C_FSI_SET_SDA0x30
+#define I2C_FSI_RESET_SDA  0x34
+
+/* cmd register */
+#define I2C_CMD_WITH_START BIT(31)
+#define I2C_CMD_WITH_ADDR  BIT(30)
+#define I2C_CMD_RD_CONTBIT(29)
+#define I2C_CMD_WITH_STOP  BIT(28)
+#define I2C_CMD_FORCELAUNCHBIT(27)
+#define I2C_CMD_ADDR   GENMASK(23, 17)
+#define I2C_CMD_READ   BIT(16)
+#define I2C_CMD_LENGENMASK(15, 0)
+
+/* mode register */
+#define I2C_MODE_CLKDIVGENMASK(31, 16)
+#define I2C_MODE_PORT  GENMASK(15, 10)
+#define I2C_MODE_ENHANCED  BIT(3)
+#define I2C_MODE_DIAG  BIT(2)
+#define I2C_MODE_PACE_ALLOWBIT(1)
+#define I2C_MODE_WRAP  BIT(0)
+
+/* watermark register */
+#define I2C_WATERMARK_HI   GENMASK(15, 12)
+#define I2C_WATERMARK_LO   GENMASK(7, 4)
+
+#define I2C_FIFO_HI_LVL4
+#define I2C_FIFO_LO_LVL4
+
+/* interrupt register */
+#define I2C_INT_INV_CMDBIT(15)
+#define I2C_INT_PARITY BIT(14)
+#define I2C_INT_BE_OVERRUN BIT(13)
+#define I2C_INT_BE_ACCESS  BIT(12)
+#define I2C_INT_LOST_ARB   BIT(11)
+#define I2C_INT_NACK   BIT(10)
+#define I2C_INT_DAT_REQBIT(9)
+#define I2C_INT_CMD_COMP   BIT(8)
+#define I2C_INT_STOP_ERR   BIT(7)
+#define I2C_INT_BUSY   BIT(6)
+#define I2C_INT_IDLE   BIT(5)
+
+#define I2C_INT_ENABLE 0xff80
+#define I2C_INT_ERR0xfcc0
+
+/* status register */
+#define I2C_STAT_INV_CMD   BIT(31)
+#define I2C_STAT_PARITYBIT(30)
+#define I2C_STAT_BE_OVERRUNBIT(29)
+#define I2C_STAT_BE_ACCESS BIT(28)
+#define I2C_STAT_LOST_ARB  BIT(27)
+#define I2C_STAT_NACK  BIT(26)
+#define I2C_STAT_DAT_REQ   BIT(25)
+#define I2C_STAT_CMD_COMP  BIT(24)
+#define I2C_STAT_STOP_ERR  BIT(23)
+#define I2C_STAT_MAX_PORT  GENMASK(19, 16

[PATCH v8 5/7] i2c: fsi: Add transfer implementation

2018-05-30 Thread Eddie James
Execute I2C transfers from the FSI-attached I2C master. Use polling
instead of interrupts as we have no hardware IRQ over FSI.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 196 ++-
 1 file changed, 194 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index e5dd0f7..f24e4b9 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -149,6 +149,7 @@ struct fsi_i2c_port {
struct i2c_adapter  adapter;
struct fsi_i2c_master   *master;
u16 port;
+   u16 xfrd;
 };
 
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -224,6 +225,100 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
 }
 
+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
+bool stop)
+{
+   struct fsi_i2c_master *i2c = port->master;
+   u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+
+   port->xfrd = 0;
+
+   if (msg->flags & I2C_M_RD)
+   cmd |= I2C_CMD_READ;
+
+   if (stop || msg->flags & I2C_M_STOP)
+   cmd |= I2C_CMD_WITH_STOP;
+
+   cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr >> 1);
+   cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);
+
+   return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+}
+
+static int fsi_i2c_get_op_bytes(int op_bytes)
+{
+   /* fsi is limited to max 4 byte aligned ops */
+   if (op_bytes > 4)
+   return 4;
+   else if (op_bytes == 3)
+   return 2;
+   else
+   return op_bytes;
+}
+
+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+   int write;
+   int rc;
+   struct fsi_i2c_master *i2c = port->master;
+   int bytes_to_write = i2c->fifo_size - fifo_count;
+   int bytes_remaining = msg->len - port->xfrd;
+
+   bytes_to_write = min(bytes_to_write, bytes_remaining);
+
+   while (bytes_to_write) {
+   write = fsi_i2c_get_op_bytes(bytes_to_write);
+
+   rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
+ &msg->buf[port->xfrd], write);
+   if (rc)
+   return rc;
+
+   port->xfrd += write;
+   bytes_to_write -= write;
+   }
+
+   return 0;
+}
+
+static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+u8 fifo_count)
+{
+   int read;
+   int rc;
+   struct fsi_i2c_master *i2c = port->master;
+   int bytes_to_read;
+   int xfr_remaining = msg->len - port->xfrd;
+   u32 dummy;
+
+   bytes_to_read = min_t(int, fifo_count, xfr_remaining);
+
+   while (bytes_to_read) {
+   read = fsi_i2c_get_op_bytes(bytes_to_read);
+
+   if (xfr_remaining) {
+   rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
+&msg->buf[port->xfrd], read);
+   if (rc)
+   return rc;
+
+   port->xfrd += read;
+   xfr_remaining -= read;
+   } else {
+   /* no more buffer but data in fifo, need to clear it */
+   rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
+read);
+   if (rc)
+   return rc;
+   }
+
+   bytes_to_read -= read;
+   }
+
+   return 0;
+}
+
 static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c)
 {
int i, rc;
@@ -387,17 +482,114 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 
status)
return -ETIMEDOUT;
 }
 
+static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
+struct i2c_msg *msg, u32 status)
+{
+   int rc;
+   u8 fifo_count;
+
+   if (status & I2C_STAT_ERR) {
+   rc = fsi_i2c_abort(port, status);
+   if (rc)
+   return rc;
+
+   if (status & I2C_STAT_INV_CMD)
+   return -EINVAL;
+
+   if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
+   I2C_STAT_BE_ACCESS))
+   return -EPROTO;
+
+   if (status & I2C_STAT_NACK)
+   return -ENXIO;
+
+   if (status & I2C_STAT_LOST_ARB)
+   return -EAGAIN;
+
+   if (status & I2C_STAT_STOP_ERR)
+   return -EBADMSG;
+
+   return -EIO;
+   }
+
+   if (status &

[PATCH v8 1/7] dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation

2018-05-30 Thread Eddie James
Document the bindings.

Signed-off-by: Eddie James >
Acked-by: Rob Herring 
---
 Documentation/devicetree/bindings/i2c/i2c-fsi.txt | 40 +++
 1 file changed, 40 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-fsi.txt 
b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
new file mode 100644
index 000..b1be2ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
@@ -0,0 +1,40 @@
+Device-tree bindings for FSI-attached I2C master and busses
+---
+
+Required properties:
+ - compatible = "ibm,i2c-fsi";
+ - reg = < address size >; : The FSI CFAM address and address
+ space size.
+ - #address-cells = <1>;   : Number of address cells in child
+ nodes.
+ - #size-cells = <0>;  : Number of size cells in child nodes.
+ - child nodes : Nodes to describe busses off the I2C
+ master.
+
+Child node required properties:
+ - reg = < port number >   : The port number on the I2C master.
+
+Child node optional properties:
+ - child nodes : Nodes to describe devices on the I2C
+ bus.
+
+Examples:
+
+i2c@1800 {
+compatible = "ibm,i2c-fsi";
+reg = < 0x1800 0x400 >;
+#address-cells = <1>;
+#size-cells = <0>;
+
+i2c-bus@0 {
+reg = <0>;
+};
+
+i2c-bus@1 {
+reg = <1>;
+
+eeprom@50 {
+compatible = "vendor,dev-name";
+};
+};
+};
-- 
1.8.3.1



[PATCH v8 3/7] i2c: fsi: Add port structures

2018-05-30 Thread Eddie James
Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports for each master are defined in the devicetree.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 90 
 1 file changed, 90 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index e1b183c..be8e15c 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -17,7 +17,9 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 
 #define FSI_ENGID_I2C  0x7
 
@@ -123,6 +125,14 @@
 struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
+   struct list_headports;
+};
+
+struct fsi_i2c_port {
+   struct list_headlist;
+   struct i2c_adapter  adapter;
+   struct fsi_i2c_master   *master;
+   u16 port;
 };
 
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -176,9 +186,38 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
 }
 
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+   int rc;
+   struct fsi_device *fsi = port->master->fsi;
+   u32 mode, dummy = 0;
+
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   if (FIELD_GET(I2C_MODE_PORT, mode) == port->port)
+   return 0;
+
+   mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
+   rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   /* reset engine when port is changed */
+   return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
 {
+   int rc;
+   struct fsi_i2c_port *port = adap->algo_data;
+
+   rc = fsi_i2c_set_port(port);
+   if (rc)
+   return rc;
+
return -EOPNOTSUPP;
 }
 
@@ -196,23 +235,73 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
 static int fsi_i2c_probe(struct device *dev)
 {
struct fsi_i2c_master *i2c;
+   struct fsi_i2c_port *port;
+   struct device_node *np;
int rc;
+   u32 port_no;
 
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
 
i2c->fsi = to_fsi_dev(dev);
+   INIT_LIST_HEAD(&i2c->ports);
 
rc = fsi_i2c_dev_init(i2c);
if (rc)
return rc;
 
+   /* Add adapter for each i2c port of the master. */
+   for_each_available_child_of_node(dev->of_node, np) {
+   rc = of_property_read_u32(np, "reg", &port_no);
+   if (rc || port_no > USHRT_MAX)
+   continue;
+
+   port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+   if (!port)
+   break;
+
+   port->master = i2c;
+   port->port = port_no;
+
+   port->adapter.owner = THIS_MODULE;
+   port->adapter.dev.of_node = np;
+   port->adapter.dev.parent = dev;
+   port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.algo_data = port;
+
+   snprintf(port->adapter.name, sizeof(port->adapter.name),
+"i2c_bus-%u", port_no);
+
+   rc = i2c_add_adapter(&port->adapter);
+   if (rc < 0) {
+   dev_err(dev, "Failed to register adapter: %d\n", rc);
+   devm_kfree(dev, port);
+   continue;
+   }
+
+   list_add(&port->list, &i2c->ports);
+   }
+
dev_set_drvdata(dev, i2c);
 
return 0;
 }
 
+static int fsi_i2c_remove(struct device *dev)
+{
+   struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+   struct fsi_i2c_port *port;
+
+   if (!list_empty(&i2c->ports)) {
+   list_for_each_entry(port, &i2c->ports, list) {
+   i2c_del_adapter(&port->adapter);
+   }
+   }
+
+   return 0;
+}
+
 static const struct fsi_device_id fsi_i2c_ids[] = {
{ FSI_ENGID_I2C, FSI_VERSION_ANY },
{ 0 }
@@ -224,6 +313,7 @@ static int fsi_i2c_probe(struct device *dev)
.name = "i2c-fsi",
.bus = &fsi_bus_type,
.probe = fsi_i2c_probe,
+   .remove = fsi_i2c_remove,
},
 };
 
-- 
1.8.3.1



[PATCH v8 4/7] i2c: fsi: Add abort and hardware reset procedures

2018-05-30 Thread Eddie James
Add abort procedure for failed transfers. Add engine and bus reset
procedures to recover from as many faults as possible.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 179 +++
 1 file changed, 179 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index be8e15c..e5dd0f7 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -12,10 +12,12 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -122,6 +124,20 @@
 #define I2C_ESTAT_SELF_BUSYBIT(6)
 #define I2C_ESTAT_VERSION  GENMASK(4, 0)
 
+/* port busy register */
+#define I2C_PORT_BUSY_RESETBIT(31)
+
+/* wait for command complete or data request */
+#define I2C_CMD_SLEEP_MAX_US   500
+#define I2C_CMD_SLEEP_MIN_US   50
+
+/* wait after reset; choose time from legacy driver */
+#define I2C_RESET_SLEEP_MAX_US 2000
+#define I2C_RESET_SLEEP_MIN_US 1000
+
+/* choose timeout length from legacy driver; it's well tested */
+#define I2C_ABORT_TIMEOUT  msecs_to_jiffies(100)
+
 struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
@@ -208,6 +224,169 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
 }
 
+static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c)
+{
+   int i, rc;
+   u32 mode, stat, ext, dummy = 0;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   mode |= I2C_MODE_DIAG;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   for (i = 0; i < 9; ++i) {
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+   if (rc)
+   return rc;
+   }
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
+   if (rc)
+   return rc;
+
+   mode &= ~I2C_MODE_DIAG;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+   if (rc)
+   return rc;
+
+   /* check for hardware fault */
+   if (!(stat & I2C_STAT_SCL_IN) || !(stat & I2C_STAT_SDA_IN)) {
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &ext);
+   if (rc)
+   return rc;
+
+   dev_err(&i2c->fsi->dev, "bus stuck status[%08X] ext[%08X]\n",
+   stat, ext);
+   }
+
+   return 0;
+}
+
+static int fsi_i2c_reset(struct fsi_i2c_master *i2c, u16 port)
+{
+   int rc;
+   u32 mode, stat, dummy = 0;
+
+   /* reset engine */
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+   if (rc)
+   return rc;
+
+   /* re-init engine */
+   rc = fsi_i2c_dev_init(i2c);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   /* set port; default after reset is 0 */
+   if (port) {
+   mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT,
+   port);
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+   }
+
+   /* reset busy register; hw workaround */
+   dummy = I2C_PORT_BUSY_RESET;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy);
+   if (rc)
+   return rc;
+
+   /* force bus reset */
+   rc = fsi_i2c_reset_bus(i2c);
+   if (rc)
+   return rc;
+
+   /* reset errors */
+   dummy = 0;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+   if (rc)
+   return rc;
+
+   /* wait for command complete */
+   usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US);
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+   if (rc)
+   return rc;
+
+   if (stat & I2C_STAT_CMD_COMP)
+   return rc;
+
+   /* failed to get command complete; reset engine a

Re: [PATCH v7 3/7] drivers/i2c: Add port structure to FSI algorithm

2018-05-31 Thread Eddie James




On 05/30/2018 04:16 PM, Benjamin Herrenschmidt wrote:

On Wed, 2018-05-30 at 10:47 -0500, Eddie James wrote:

+   if (!list_empty(&i2c->ports)) {

My gosh, this is done already in list_for_each*()

No, list_for_each_entry does NOT check if the list is empty or if the
first entry is NULL.

NULL is never valid for a list. It does however check for an empty list

It does it implicitely in the test part of the for () statement,
checking if the next pointer points back to the head.


Thanks Ben. My mistake on this one; I misread the macro. I will remove 
the check in v9... going to wait a little for any further comments now.


Thanks,
Eddie



Cheers,
Ben.






[PATCH] fsi: sbefifo: Add missing mutex_unlock

2018-07-03 Thread Eddie James
There was no unlock of the FFDC mutex.

Fixes: 9f4a8a2d7f9d ("fsi/sbefifo: Add driver for the SBE FIFO")
Signed-off-by: Eddie James 
---
 drivers/fsi/fsi-sbefifo.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index 4f076fd..a34ff99 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -194,6 +194,7 @@ static void sbefifo_dump_ffdc(struct device *dev, const 
__be32 *ffdc,
}
dev_warn(dev, 
"+---+\n");
}
+   mutex_unlock(&sbefifo_ffdc_mutex);
 }
 
 int sbefifo_parse_status(struct device *dev, u16 cmd, __be32 *response,
-- 
1.8.3.1



[PATCH v11 0/8] i2c: Add FSI-attached I2C master algorithm

2018-07-05 Thread Eddie James
This series adds an algorithm for an I2C master physically located on an FSI
slave device. The I2C master has multiple ports, each of which may be connected
to an I2C slave. Access to the I2C master registers is achieved over FSI bus.

Due to the multi-port nature of the I2C master, the driver instantiates a new
I2C adapter for each port connected to a slave. The connected ports should be
defined in the device tree under the I2C master device.

Changes since v10
 - Add myself to maintainers
 - Rework the reset path. Use generic bus recovery and only do the reset if SDA
   is low.
 - Fixed email signature on dts patch
 - Pulled in Ben H's fix for use-after-free in remove()

Changes since v9
 - Switch the status masks to use a combination of bits rather than directly
   coding the value
 - Remove the interrupt mask definition as it was unused
 - Fixed return value of master_xfer function (return number of xfrs, not 0)

Changes since v8
 - Drop unecessary else statements
 - Use i++ instead of ++i
 - Use kzalloc/kfree instead of devm_kzalloc/devm_kfree for port structure
 - Drop the list_empty check in remove

Changes since v7
 - Fix grammer in Kconfig (a -> an)
 - Change I2C registers to use BIT and GENMASK
 - Remove custom macros and use FIELD_PREP and FIELD_GET
 - Fix a few unecessary initializations and "return rc" that are always zero
 - Clean up the read/write fifo functions a bit
 - Few other clean-up items

Changes since v6
 - Remove spinlock for reset functionality; it's unecessary and doesn't work
   with the latest FSI core.
 - Use a mutex instead of a semaphore, and don't wait for timeout to get the
   lock.
 - Use usleeps instead of schedule_timeout; it's not worth the overhead when
   the wait should be very short in between sending the command and receiving
   the response.

Changes since v5
 - Fix reset functionality and do a reset after every transfer failure

Eddie James (8):
  dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation
  i2c: Add FSI-attached I2C master algorithm
  i2c: fsi: Add port structures
  i2c: fsi: Add abort and hardware reset procedures
  i2c: fsi: Add transfer implementation
  i2c: fsi: Add I2C master locking
  i2c: fsi: Add bus recovery
  MAINTAINERS: Add Eddie as the maintainer for the FSI-attached I2C
driver

 Documentation/devicetree/bindings/i2c/i2c-fsi.txt |  40 ++
 MAINTAINERS   |   8 +
 drivers/i2c/busses/Kconfig|  11 +
 drivers/i2c/busses/Makefile   |   1 +
 drivers/i2c/busses/i2c-fsi.c  | 753 ++
 5 files changed, 813 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

-- 
1.8.3.1



[PATCH v11 6/8] i2c: fsi: Add I2C master locking

2018-07-05 Thread Eddie James
Since there are many ports per master, each with it's own adapter and
chardev, we need some locking to prevent transfers from changing the
master state while other transfers are in progress.

Signed-off-by: Eddie James 
Reviewed-by: Andy Shevchenko 
---
 drivers/i2c/busses/i2c-fsi.c | 16 
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 6cb4602..5520251 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -148,6 +149,7 @@ struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
struct list_headports;
+   struct mutexlock;
 };
 
 struct fsi_i2c_port {
@@ -486,11 +488,14 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs,
int i, rc;
unsigned long start_time;
struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *master = port->master;
struct i2c_msg *msg;
 
+   mutex_lock(&master->lock);
+
rc = fsi_i2c_set_port(port);
if (rc)
-   return rc;
+   goto unlock;
 
for (i = 0; i < num; i++) {
msg = msgs + i;
@@ -498,15 +503,17 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs,
 
rc = fsi_i2c_start(port, msg, i == num - 1);
if (rc)
-   return rc;
+   goto unlock;
 
rc = fsi_i2c_wait(port, msg,
  adap->timeout - (jiffies - start_time));
if (rc)
-   return rc;
+   goto unlock;
}
 
-   return num;
+unlock:
+   mutex_unlock(&master->lock);
+   return rc ? : num;
 }
 
 static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
@@ -533,6 +540,7 @@ static int fsi_i2c_probe(struct device *dev)
if (!i2c)
return -ENOMEM;
 
+   mutex_init(&i2c->lock);
i2c->fsi = to_fsi_dev(dev);
INIT_LIST_HEAD(&i2c->ports);
 
-- 
1.8.3.1



[PATCH v11 2/8] i2c: Add FSI-attached I2C master algorithm

2018-07-05 Thread Eddie James
Add register definitions for FSI-attached I2C master and functions to
access those registers over FSI. Add an FSI driver so that our I2C bus
is probed up during an FSI scan.

Signed-off-by: Eddie James 
Reviewed-by: Andy Shevchenko 
---
 drivers/i2c/busses/Kconfig   |  11 ++
 drivers/i2c/busses/Makefile  |   1 +
 drivers/i2c/busses/i2c-fsi.c | 240 +++
 3 files changed, 252 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e7a4d28..48b5de7 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1327,4 +1327,15 @@ config I2C_ZX2967
  This driver can also be built as a module. If so, the module will be
  called i2c-zx2967.
 
+config I2C_FSI
+   tristate "FSI I2C driver"
+   depends on FSI
+   help
+ Driver for FSI bus attached I2C masters. These are I2C masters that
+ are connected to the system over an FSI bus, instead of the more
+ common PCI or MMIO interface.
+
+ This driver can also be built as a module. If so, the module will be
+ called as i2c-fsi.
+
 endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5a86914..4909fd6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -137,5 +137,6 @@ obj-$(CONFIG_I2C_PCA_ISA)   += i2c-pca-isa.o
 obj-$(CONFIG_I2C_SIBYTE)   += i2c-sibyte.o
 obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
 obj-$(CONFIG_SCx200_ACB)   += scx200_acb.o
+obj-$(CONFIG_I2C_FSI)  += i2c-fsi.o
 
 ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
new file mode 100644
index 000..694bbb4
--- /dev/null
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FSI-attached I2C master algorithm
+ *
+ * Copyright 2018 IBM Corporation
+ *
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define FSI_ENGID_I2C  0x7
+
+#define I2C_DEFAULT_CLK_DIV6
+
+/* i2c registers */
+#define I2C_FSI_FIFO   0x00
+#define I2C_FSI_CMD0x04
+#define I2C_FSI_MODE   0x08
+#define I2C_FSI_WATER_MARK 0x0C
+#define I2C_FSI_INT_MASK   0x10
+#define I2C_FSI_INT_COND   0x14
+#define I2C_FSI_OR_INT_MASK0x14
+#define I2C_FSI_INTS   0x18
+#define I2C_FSI_AND_INT_MASK   0x18
+#define I2C_FSI_STAT   0x1C
+#define I2C_FSI_RESET_I2C  0x1C
+#define I2C_FSI_ESTAT  0x20
+#define I2C_FSI_RESET_ERR  0x20
+#define I2C_FSI_RESID_LEN  0x24
+#define I2C_FSI_SET_SCL0x24
+#define I2C_FSI_PORT_BUSY  0x28
+#define I2C_FSI_RESET_SCL  0x2C
+#define I2C_FSI_SET_SDA0x30
+#define I2C_FSI_RESET_SDA  0x34
+
+/* cmd register */
+#define I2C_CMD_WITH_START BIT(31)
+#define I2C_CMD_WITH_ADDR  BIT(30)
+#define I2C_CMD_RD_CONTBIT(29)
+#define I2C_CMD_WITH_STOP  BIT(28)
+#define I2C_CMD_FORCELAUNCHBIT(27)
+#define I2C_CMD_ADDR   GENMASK(23, 17)
+#define I2C_CMD_READ   BIT(16)
+#define I2C_CMD_LENGENMASK(15, 0)
+
+/* mode register */
+#define I2C_MODE_CLKDIVGENMASK(31, 16)
+#define I2C_MODE_PORT  GENMASK(15, 10)
+#define I2C_MODE_ENHANCED  BIT(3)
+#define I2C_MODE_DIAG  BIT(2)
+#define I2C_MODE_PACE_ALLOWBIT(1)
+#define I2C_MODE_WRAP  BIT(0)
+
+/* watermark register */
+#define I2C_WATERMARK_HI   GENMASK(15, 12)
+#define I2C_WATERMARK_LO   GENMASK(7, 4)
+
+#define I2C_FIFO_HI_LVL4
+#define I2C_FIFO_LO_LVL4
+
+/* interrupt register */
+#define I2C_INT_INV_CMDBIT(15)
+#define I2C_INT_PARITY BIT(14)
+#define I2C_INT_BE_OVERRUN BIT(13)
+#define I2C_INT_BE_ACCESS  BIT(12)
+#define I2C_INT_LOST_ARB   BIT(11)
+#define I2C_INT_NACK   BIT(10)
+#define I2C_INT_DAT_REQBIT(9)
+#define I2C_INT_CMD_COMP   BIT(8)
+#define I2C_INT_STOP_ERR   BIT(7)
+#define I2C_INT_BUSY   BIT(6)
+#define I2C_INT_IDLE   BIT(5)
+
+/* status register */
+#define I2C_STAT_INV_CMD   BIT(31)
+#define I2C_STAT_PARITYBIT(30)
+#define I2C_STAT_BE_OVERRUNBIT(29)
+#define I2C_STAT_BE_ACCESS BIT(28)
+#define I2C_STAT_LOST_ARB  BIT(27)
+#define I2C_STAT_NACK  BIT(26)
+#define I2C_STAT_DAT_REQ   BIT(25)
+#define I2C_STAT_CMD_COMP  BIT(24)
+#define I2C_STAT_STOP_ERR  BIT(23)
+#define I2C_STAT_MAX_PORT  GENMASK(19, 16)
+#define I2C_STAT_ANY_INT   BIT(15)
+#define I2C_S

[PATCH v11 8/8] MAINTAINERS: Add Eddie as the maintainer for the FSI-attached I2C driver

2018-07-05 Thread Eddie James
Signed-off-by: Eddie James 
---
 MAINTAINERS | 8 
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 10fd4c0..63f6d41 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5888,6 +5888,14 @@ F:   fs/crypto/
 F: include/linux/fscrypt*.h
 F: Documentation/filesystems/fscrypt.rst
 
+FSI-ATTACHED I2C DRIVER
+M: Eddie James 
+L: linux-...@vger.kernel.org
+L: open...@lists.ozlabs.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/i2c/busses/i2c-fsi.c
+F: Documentation/devicetree/bindings/i2c/i2c-fsi.txt
+
 FSNOTIFY: FILESYSTEM NOTIFICATION INFRASTRUCTURE
 M: Jan Kara 
 R: Amir Goldstein 
-- 
1.8.3.1



[PATCH v11 3/8] i2c: fsi: Add port structures

2018-07-05 Thread Eddie James
Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports for each master are defined in the devicetree.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 91 
 1 file changed, 91 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 694bbb4..89b7349 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -17,7 +17,10 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #define FSI_ENGID_I2C  0x7
 
@@ -128,6 +131,14 @@
 struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
+   struct list_headports;
+};
+
+struct fsi_i2c_port {
+   struct list_headlist;
+   struct i2c_adapter  adapter;
+   struct fsi_i2c_master   *master;
+   u16 port;
 };
 
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -181,9 +192,38 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
 }
 
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+   int rc;
+   struct fsi_device *fsi = port->master->fsi;
+   u32 mode, dummy = 0;
+
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   if (FIELD_GET(I2C_MODE_PORT, mode) == port->port)
+   return 0;
+
+   mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
+   rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   /* reset engine when port is changed */
+   return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
 {
+   int rc;
+   struct fsi_i2c_port *port = adap->algo_data;
+
+   rc = fsi_i2c_set_port(port);
+   if (rc)
+   return rc;
+
return -EOPNOTSUPP;
 }
 
@@ -202,23 +242,73 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
 static int fsi_i2c_probe(struct device *dev)
 {
struct fsi_i2c_master *i2c;
+   struct fsi_i2c_port *port;
+   struct device_node *np;
int rc;
+   u32 port_no;
 
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
 
i2c->fsi = to_fsi_dev(dev);
+   INIT_LIST_HEAD(&i2c->ports);
 
rc = fsi_i2c_dev_init(i2c);
if (rc)
return rc;
 
+   /* Add adapter for each i2c port of the master. */
+   for_each_available_child_of_node(dev->of_node, np) {
+   rc = of_property_read_u32(np, "reg", &port_no);
+   if (rc || port_no > USHRT_MAX)
+   continue;
+
+   port = kzalloc(sizeof(*port), GFP_KERNEL);
+   if (!port)
+   break;
+
+   port->master = i2c;
+   port->port = port_no;
+
+   port->adapter.owner = THIS_MODULE;
+   port->adapter.dev.of_node = np;
+   port->adapter.dev.parent = dev;
+   port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.algo_data = port;
+
+   snprintf(port->adapter.name, sizeof(port->adapter.name),
+"i2c_bus-%u", port_no);
+
+   rc = i2c_add_adapter(&port->adapter);
+   if (rc < 0) {
+   dev_err(dev, "Failed to register adapter: %d\n", rc);
+   kfree(port);
+   continue;
+   }
+
+   list_add(&port->list, &i2c->ports);
+   }
+
dev_set_drvdata(dev, i2c);
 
return 0;
 }
 
+static int fsi_i2c_remove(struct device *dev)
+{
+   struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+   struct fsi_i2c_port *port, *tmp;
+
+   list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
+   list_del(&port->list);
+   i2c_del_adapter(&port->adapter);
+   kfree(port);
+   }
+
+   return 0;
+}
+
 static const struct fsi_device_id fsi_i2c_ids[] = {
{ FSI_ENGID_I2C, FSI_VERSION_ANY },
{ }
@@ -230,6 +320,7 @@ static int fsi_i2c_probe(struct device *dev)
.name = "i2c-fsi",
.bus = &fsi_bus_type,
.probe = fsi_i2c_probe,
+   .remove = fsi_i2c_remove,
},
 };
 
-- 
1.8.3.1



[PATCH v11 5/8] i2c: fsi: Add transfer implementation

2018-07-05 Thread Eddie James
Execute I2C transfers from the FSI-attached I2C master. Use polling
instead of interrupts as we have no hardware IRQ over FSI.

Signed-off-by: Eddie James 
Reviewed-by: Andy Shevchenko 
---
 drivers/i2c/busses/i2c-fsi.c | 195 ++-
 1 file changed, 193 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 8f1e611..6cb4602 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -155,6 +155,7 @@ struct fsi_i2c_port {
struct i2c_adapter  adapter;
struct fsi_i2c_master   *master;
u16 port;
+   u16 xfrd;
 };
 
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -230,6 +231,99 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
 }
 
+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
+bool stop)
+{
+   struct fsi_i2c_master *i2c = port->master;
+   u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+
+   port->xfrd = 0;
+
+   if (msg->flags & I2C_M_RD)
+   cmd |= I2C_CMD_READ;
+
+   if (stop || msg->flags & I2C_M_STOP)
+   cmd |= I2C_CMD_WITH_STOP;
+
+   cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr >> 1);
+   cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);
+
+   return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+}
+
+static int fsi_i2c_get_op_bytes(int op_bytes)
+{
+   /* fsi is limited to max 4 byte aligned ops */
+   if (op_bytes > 4)
+   return 4;
+   else if (op_bytes == 3)
+   return 2;
+   return op_bytes;
+}
+
+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+   int write;
+   int rc;
+   struct fsi_i2c_master *i2c = port->master;
+   int bytes_to_write = i2c->fifo_size - fifo_count;
+   int bytes_remaining = msg->len - port->xfrd;
+
+   bytes_to_write = min(bytes_to_write, bytes_remaining);
+
+   while (bytes_to_write) {
+   write = fsi_i2c_get_op_bytes(bytes_to_write);
+
+   rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
+ &msg->buf[port->xfrd], write);
+   if (rc)
+   return rc;
+
+   port->xfrd += write;
+   bytes_to_write -= write;
+   }
+
+   return 0;
+}
+
+static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+u8 fifo_count)
+{
+   int read;
+   int rc;
+   struct fsi_i2c_master *i2c = port->master;
+   int bytes_to_read;
+   int xfr_remaining = msg->len - port->xfrd;
+   u32 dummy;
+
+   bytes_to_read = min_t(int, fifo_count, xfr_remaining);
+
+   while (bytes_to_read) {
+   read = fsi_i2c_get_op_bytes(bytes_to_read);
+
+   if (xfr_remaining) {
+   rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
+&msg->buf[port->xfrd], read);
+   if (rc)
+   return rc;
+
+   port->xfrd += read;
+   xfr_remaining -= read;
+   } else {
+   /* no more buffer but data in fifo, need to clear it */
+   rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
+read);
+   if (rc)
+   return rc;
+   }
+
+   bytes_to_read -= read;
+   }
+
+   return 0;
+}
+
 static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
 {
int rc;
@@ -305,17 +399,114 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 
status)
return -ETIMEDOUT;
 }
 
+static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
+struct i2c_msg *msg, u32 status)
+{
+   int rc;
+   u8 fifo_count;
+
+   if (status & I2C_STAT_ERR) {
+   rc = fsi_i2c_abort(port, status);
+   if (rc)
+   return rc;
+
+   if (status & I2C_STAT_INV_CMD)
+   return -EINVAL;
+
+   if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
+   I2C_STAT_BE_ACCESS))
+   return -EPROTO;
+
+   if (status & I2C_STAT_NACK)
+   return -ENXIO;
+
+   if (status & I2C_STAT_LOST_ARB)
+   return -EAGAIN;
+
+   if (status & I2C_STAT_STOP_ERR)
+   return -EBADMSG;
+
+   return -EIO;
+   }
+
+   if (status &

[PATCH v11 7/8] i2c: fsi: Add bus recovery

2018-07-05 Thread Eddie James
Bus recovery should reset the bus with the standard i2c recovery
procedure. Populate the necessary fields so that the standard procedure
can perform the reset.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 132 +++
 1 file changed, 132 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 5520251..42c387d 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -326,6 +326,115 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, 
struct i2c_msg *msg,
return 0;
 }
 
+static int fsi_i2c_get_scl(struct i2c_adapter *adap)
+{
+   u32 stat = 0;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *i2c = port->master;
+
+   fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+
+   return !!(stat & I2C_STAT_SCL_IN);
+}
+
+static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
+{
+   u32 dummy = 0;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *i2c = port->master;
+
+   if (val)
+   fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+   else
+   fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+}
+
+static int fsi_i2c_get_sda(struct i2c_adapter *adap)
+{
+   u32 stat = 0;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *i2c = port->master;
+
+   fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+
+   return !!(stat & I2C_STAT_SDA_IN);
+}
+
+static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
+{
+   u32 dummy = 0;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *i2c = port->master;
+
+   if (val)
+   fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
+   else
+   fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
+}
+
+static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
+{
+   int rc;
+   u32 mode;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *i2c = port->master;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return;
+
+   mode |= I2C_MODE_DIAG;
+   fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+}
+
+static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
+{
+   int rc;
+   u32 mode;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *i2c = port->master;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return;
+
+   mode &= ~I2C_MODE_DIAG;
+   fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+}
+
+static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
+struct fsi_i2c_port *port)
+{
+   int rc;
+   u32 stat, dummy = 0;
+
+   /* force bus reset, ignore errors */
+   i2c_recover_bus(&port->adapter);
+
+   /* reset errors */
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+   if (rc)
+   return rc;
+
+   /* wait for command complete */
+   usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US);
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+   if (rc)
+   return rc;
+
+   if (stat & I2C_STAT_CMD_COMP)
+   return 0;
+
+   /* failed to get command complete; reset engine again */
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+   if (rc)
+   return rc;
+
+   /* re-init engine again */
+   return fsi_i2c_dev_init(i2c);
+}
+
 static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
 {
int rc;
@@ -368,6 +477,7 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 
status)
int rc;
unsigned long start;
u32 cmd = I2C_CMD_WITH_STOP;
+   u32 stat;
struct fsi_i2c_master *i2c = port->master;
struct fsi_device *fsi = i2c->fsi;
 
@@ -375,6 +485,17 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 
status)
if (rc)
return rc;
 
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &stat);
+   if (rc)
+   return rc;
+
+   /* if sda is low, peform full bus reset */
+   if (!(stat & I2C_STAT_SDA_IN)) {
+   rc = fsi_i2c_reset_bus(i2c, port);
+   if (rc)
+   return rc;
+   }
+
/* skip final stop command for these errors */
if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
return 0;
@@ -523,6 +644,16 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_BLOCK_DATA;
 }
 
+static struct i2c_bus_recovery_info

[PATCH v11 4/8] i2c: fsi: Add abort and hardware reset procedures

2018-07-05 Thread Eddie James
Add abort procedure for failed transfers. Add engine reset procedure
that is executed during the abort to recover from various fault
conditions.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 91 
 1 file changed, 91 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 89b7349..8f1e611 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -12,10 +12,12 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -128,6 +130,20 @@
 #define I2C_ESTAT_SELF_BUSYBIT(6)
 #define I2C_ESTAT_VERSION  GENMASK(4, 0)
 
+/* port busy register */
+#define I2C_PORT_BUSY_RESETBIT(31)
+
+/* wait for command complete or data request */
+#define I2C_CMD_SLEEP_MAX_US   500
+#define I2C_CMD_SLEEP_MIN_US   50
+
+/* wait after reset; choose time from legacy driver */
+#define I2C_RESET_SLEEP_MAX_US 2000
+#define I2C_RESET_SLEEP_MIN_US 1000
+
+/* choose timeout length from legacy driver; it's well tested */
+#define I2C_ABORT_TIMEOUT  msecs_to_jiffies(100)
+
 struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
@@ -214,6 +230,81 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
 }
 
+static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
+{
+   int rc;
+   u32 mode, dummy = 0;
+
+   /* reset engine */
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+   if (rc)
+   return rc;
+
+   /* re-init engine */
+   rc = fsi_i2c_dev_init(i2c);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   /* set port; default after reset is 0 */
+   if (port) {
+   mode &= ~I2C_MODE_PORT;
+   mode |= FIELD_PREP(I2C_MODE_PORT, port);
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+   }
+
+   /* reset busy register; hw workaround */
+   dummy = I2C_PORT_BUSY_RESET;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy);
+   if (rc)
+   return rc;
+
+   return 0;
+}
+
+static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
+{
+   int rc;
+   unsigned long start;
+   u32 cmd = I2C_CMD_WITH_STOP;
+   struct fsi_i2c_master *i2c = port->master;
+   struct fsi_device *fsi = i2c->fsi;
+
+   rc = fsi_i2c_reset_engine(i2c, port->port);
+   if (rc)
+   return rc;
+
+   /* skip final stop command for these errors */
+   if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
+   return 0;
+
+   /* write stop command */
+   rc = fsi_i2c_write_reg(fsi, I2C_FSI_CMD, &cmd);
+   if (rc)
+   return rc;
+
+   /* wait until we see command complete in the master */
+   start = jiffies;
+
+   do {
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &status);
+   if (rc)
+   return rc;
+
+   if (status & I2C_STAT_CMD_COMP)
+   return 0;
+
+   usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
+   } while (time_after(start + I2C_ABORT_TIMEOUT, jiffies));
+
+   return -ETIMEDOUT;
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
 {
-- 
1.8.3.1



[PATCH v11 1/8] dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation

2018-07-05 Thread Eddie James
Document the bindings.

Signed-off-by: Eddie James 
Acked-by: Rob Herring 
---
 Documentation/devicetree/bindings/i2c/i2c-fsi.txt | 40 +++
 1 file changed, 40 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-fsi.txt 
b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
new file mode 100644
index 000..b1be2ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
@@ -0,0 +1,40 @@
+Device-tree bindings for FSI-attached I2C master and busses
+---
+
+Required properties:
+ - compatible = "ibm,i2c-fsi";
+ - reg = < address size >; : The FSI CFAM address and address
+ space size.
+ - #address-cells = <1>;   : Number of address cells in child
+ nodes.
+ - #size-cells = <0>;  : Number of size cells in child nodes.
+ - child nodes : Nodes to describe busses off the I2C
+ master.
+
+Child node required properties:
+ - reg = < port number >   : The port number on the I2C master.
+
+Child node optional properties:
+ - child nodes : Nodes to describe devices on the I2C
+ bus.
+
+Examples:
+
+i2c@1800 {
+compatible = "ibm,i2c-fsi";
+reg = < 0x1800 0x400 >;
+#address-cells = <1>;
+#size-cells = <0>;
+
+i2c-bus@0 {
+reg = <0>;
+};
+
+i2c-bus@1 {
+reg = <1>;
+
+eeprom@50 {
+compatible = "vendor,dev-name";
+};
+};
+};
-- 
1.8.3.1



Re: [PATCH v10 4/7] i2c: fsi: Add abort and hardware reset procedures

2018-07-05 Thread Eddie James




On 07/02/2018 01:15 PM, Wolfram Sang wrote:

Hi Eddie,


I think this is a way too aggressive recovery. Your are doing the 9
pulse toggles basically on any error while this is only when the device
keeps SDA low and you want to recover from that. If SDA is not stuck
low, sending a STOP should do. Or do you have a known case where this is
not going to work?

It is aggressive, but I don't see the harm in doing this on every error.

Well, as it happens, I just fixed such a case. Please check these patch
series and elinux wiki pages:

===

(new fault injector)
[PATCH v2 0/2] i2c: gpio: fault-injector: add new injector

(actual recovery fix)
[PATCH 0/2] i2c: recovery: make sure pulses are not misinterpreted

===

And here is the new elinux wiki page to describe my findings:

https://elinux.org/Tests:I2C-bus-recovery-write-byte-fix

Also, the previous pages have been updated to reflect the latest status:

https://elinux.org/Tests:I2C-fault-injection
https://elinux.org/Tests:I2C-bus-recovery

To sum it up: This is a proven case where uncontrolled bus recovery can
result into a bogus write!


There are some other error conditions with this hardware which may require
the clock toggling, such as "bus arbitration lost." I think this is the

Why is that? In my understanding, recovery is *only* needed when SDA is
stuck low. If SDA is high, sending STOP should do. If not, it needs to
be researched why.


safest option for this hardware, and this routine has been tested for many
years.

I remember having a similar argument with Joakim Tjernlund a while ago.
I recently re-read our argument, yet I still keep my position: I don't
want to do $random things to recover, just a tested and well understood
procedure. And in that thread, I was never given a test case.


Also, you implement the pulse toggling manually. Can't you just populate
{get|set}_{scl|sda} and use the generic routine we have in the core?

I see that the generic implementation breaks the loop if it sees the clock
isn't high after setting it, or if SDA goes high. I think it's safer to
finish the reset for our hardware. Plus, we actually have different

Why do you think it is safer? What is the test case for that? I think
one really should do check SDA! See above, you might trigger a write
otherwise. If this breaks something for you, I am looking forward to
discuss it.


registers for setting 0 or 1 to the clock/data, so we save some cpu cycles
by doing it directly instead of implementing set_scl/sda and having to check
val every time :)

Correctness comes above all here. And I am afraid your implementation is
not correct.


If you feel very strongly that this recovery procedure needs to be reduced,
then I will work on that and have to do some extensive testing.

I am open for discussion, yet I also feel strong about it. The reason
why the recovery procedure is moved into the core is to have one working
and understood bit-banging algorithm which all drivers can rely on. If
all drivers implement their custom version, they might miss gory details
like the above write_byte fix.

I do understand this might cause testing effort for you, I am sorry for
the delay it causes. However, my goal as a maintainer is to have a
reliable recovery mechanism, for your driver as well as for all drivers.

I hope this is understandable. BTW if you want this driver upstream
soon, then it may be an idea to resend it without any bus recovery and
then we can work on it incrementally.


Thanks for the details. I have sent up a new series which will only do 
the bus reset if SDA is low. With our current hardware configuration, 
this *should* be sufficient to recover all the possible errors. However, 
there are configurations where it will not be enough, in which case 
getting the data line stuck high or clock line stuck either high or low 
can occur, necessitating the full reset. But since I can't demonstrate 
those at the moment, I can't argue to include that now :)


Thanks again,
Eddie



Kind regards and thanks,

Wolfram




Re: [PATCH v10 5/7] i2c: fsi: Add transfer implementation

2018-07-05 Thread Eddie James




On 07/02/2018 01:24 PM, Wolfram Sang wrote:

+   if (msg->flags & I2C_M_RD)
+   cmd |= I2C_CMD_READ;

Since you support MANGLING, I'd think you can easily support
I2C_M_REV_DIR_ADDR here, too?

Hm, I don't really understand the purpose of that flag. From the docs:

This toggles the Rd/Wr flag. That is, if you want to do a write, but
     need to emit an Rd instead of a Wr, or vice versa, you set this
     flag. For example:
S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P

I don't think our hardware supports this type of operation.

I'd think something like this should do:

if (msg->flags & I2C_M_REV_DIR_ADDR)
cmd ^= I2C_CMD_READ;


I meant that the hardware cannot interpret this, it would be a 
meaningless command unfortunately.








Re: [RFC PATCH 5/5] fsi/scom: Major overhaul

2018-06-13 Thread Eddie James




On 06/12/2018 12:19 AM, Benjamin Herrenschmidt wrote:

This was too hard to split ... this adds a number of features
to the SCOM user interface:

  - Support for indirect SCOMs

  - read()/write() interface now handle errors and retries

  - New ioctl() "raw" interface for use by debuggers

Signed-off-by: Benjamin Herrenschmidt 
---
  drivers/fsi/fsi-scom.c   | 424 ---
  include/uapi/linux/fsi.h |  56 ++
  2 files changed, 450 insertions(+), 30 deletions(-)
  create mode 100644 include/uapi/linux/fsi.h

diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index e98573ecdae1..39c74351f1bf 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -24,6 +24,8 @@
  #include 
  #include 



+
+static int scom_reset(struct scom_device *scom, void __user *argp)
+{
+   uint32_t flags, dummy = -1;
+   int rc = 0;
+
+   if (get_user(flags, (__u32 __user *)argp))
+   return -EFAULT;
+   if (flags & SCOM_RESET_PIB)
+   rc = fsi_device_write(scom->fsi_dev, SCOM_PIB_RESET_REG, &dummy,
+ sizeof(uint32_t));


I realize this is a user requested flag but I believe the BMC is never 
supposed to issue this type of reset, due to the possibility of breaking 
stuff on the host side. Not sure if it should even be available?


Otherwise, looks good!
Thanks,
Eddie


+   if (!rc && (flags & (SCOM_RESET_PIB | SCOM_RESET_INTF)))
+   rc = fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, 
&dummy,
+ sizeof(uint32_t));
+   return rc;
+}
+
+static int scom_check(struct scom_device *scom, void __user *argp)
+{
+   /* Still need to find out how to get "protected" */
+   return put_user(SCOM_CHECK_SUPPORTED, (__u32 __user *)argp);
+}
+
+static long scom_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+   struct miscdevice *mdev = file->private_data;
+   struct scom_device *scom = to_scom_dev(mdev);
+   void __user *argp = (void __user *)arg;
+   int rc = -ENOTTY;
+
+   mutex_lock(&scom->lock);
+   switch(cmd) {
+   case FSI_SCOM_CHECK:
+   rc = scom_check(scom, argp);
+   break;
+   case FSI_SCOM_READ:
+   rc = scom_raw_read(scom, argp);
+   break;
+   case FSI_SCOM_WRITE:
+   rc = scom_raw_write(scom, argp);
+   break;
+   case FSI_SCOM_RESET:
+   rc = scom_reset(scom, argp);
+   break;
+   }
+   mutex_unlock(&scom->lock);
+   return rc;
+}
+
  static const struct file_operations scom_fops = {
-   .owner  = THIS_MODULE,
-   .llseek = scom_llseek,
-   .read   = scom_read,
-   .write  = scom_write,
+   .owner  = THIS_MODULE,
+   .llseek = scom_llseek,
+   .read   = scom_read,
+   .write  = scom_write,
+   .unlocked_ioctl = scom_ioctl,
  };

  static int scom_probe(struct device *dev)
diff --git a/include/uapi/linux/fsi.h b/include/uapi/linux/fsi.h
new file mode 100644
index ..6008d93f2e48
--- /dev/null
+++ b/include/uapi/linux/fsi.h
@@ -0,0 +1,56 @@
+#ifndef _UAPI_LINUX_FSI_H
+#define _UAPI_LINUX_FSI_H
+
+#include 
+
+/*
+ * /dev/scom "raw" ioctl interface
+ *
+ * The driver supports a high level "read/write" interface which
+ * handles retries and converts the status to Linux error codes,
+ * however low level tools an debugger need to access the "raw"
+ * HW status information and interpret it themselves, so this
+ * ioctl interface is also provided for their use case.
+ */
+
+/* Structure for SCOM read/write */
+struct scom_access {
+   __u64   addr;   /* SCOM address, supports indirect */
+   __u64   data;   /* SCOM data (in for write, out for read) */
+   __u64   mask;   /* Data mask for writes */
+   __u32   intf_errors;/* Interface error flags */
+#define SCOM_INTF_ERR_PARITY   0x0001 /* Parity error */
+#define SCOM_INTF_ERR_PROTECTION   0x0002 /* Blocked by secure boot */
+#define SCOM_INTF_ERR_ABORT0x0004 /* PIB reset during access */
+#define SCOM_INTF_ERR_UNKNOWN  0x8000 /* Unknown error */
+   /*
+* Note: Any other bit set in intf_errors need to be considered as an
+* error. Future implementations may define new error conditions. The
+* pib_status below is only valid if intf_errors is 0.
+*/
+   __u8pib_status; /* 3-bit PIB status */
+#define SCOM_PIB_SUCCESS   0   /* Access successful */
+#define SCOM_PIB_BLOCKED   1   /* PIB blocked, pls retry */
+#define SCOM_PIB_OFFLINE   2   /* Chiplet offline */
+#define SCOM_PIB_PARTIAL   3   /* Partial good */
+#define SCOM_PIB_BAD_ADDR  4   /* Invalid address */
+#define SCOM_PIB_CLK_ERR   5   /* Clock error */
+#define SCOM_PIB_PARITY_ERR6   /* Parit

Re: [RFC PATCH 4/5] fsi/scom: Add register definitions

2018-06-13 Thread Eddie James




On 06/12/2018 12:19 AM, Benjamin Herrenschmidt wrote:

Add a few more register and bit definitions, also define and use
SCOM_READ_CMD (which is 0 but it makes the code clearer)

Signed-off-by: Benjamin Herrenschmidt 


Reviewed-by: Eddie James 


---
  drivers/fsi/fsi-scom.c | 19 ++-
  1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index 6ddfb6021420..e98573ecdae1 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -30,8 +30,25 @@
  #define SCOM_DATA0_REG0x00
  #define SCOM_DATA1_REG0x04
  #define SCOM_CMD_REG  0x08
+#define SCOM_FSI2PIB_RESET_REG 0x18
+#define SCOM_STATUS_REG0x1C /* Read */
+#define SCOM_PIB_RESET_REG 0x1C /* Write */

+/* Command register */
  #define SCOM_WRITE_CMD0x8000
+#define SCOM_READ_CMD  0x
+
+/* Status register bits */
+#define SCOM_STATUS_ERR_SUMMARY0x8000
+#define SCOM_STATUS_PROTECTION 0x0100
+#define SCOM_STATUS_PIB_ABORT  0x0010
+#define SCOM_STATUS_PIB_RESP_MASK  0x7000
+#define SCOM_STATUS_PIB_RESP_SHIFT 12
+
+#define SCOM_STATUS_ANY_ERR(SCOM_STATUS_ERR_SUMMARY | \
+SCOM_STATUS_PROTECTION | \
+SCOM_STATUS_PIB_ABORT | \
+SCOM_STATUS_PIB_RESP_MASK)

  struct scom_device {
struct list_head link;
@@ -85,7 +102,7 @@ static int get_scom(struct scom_device *scom_dev, uint64_t 
*value,

mutex_lock(&scom_dev->lock);
*value = 0ULL;
-   data = cpu_to_be32(addr);
+   data = cpu_to_be32(SCOM_READ_CMD | addr);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
sizeof(uint32_t));
if (rc)




Re: [RFC PATCH 3/5] fsi/scom: Fixup endian annotations

2018-06-13 Thread Eddie James




On 06/12/2018 12:19 AM, Benjamin Herrenschmidt wrote:

Use the proper annotated type __be32 and fixup the
accessor used for get_scom()

Signed-off-by: Benjamin Herrenschmidt 


Reviewed-by: Eddie James 


---
  drivers/fsi/fsi-scom.c | 9 -
  1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index 8a608db0aa07..6ddfb6021420 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -51,8 +51,8 @@ static DEFINE_IDA(scom_ida);
  static int put_scom(struct scom_device *scom_dev, uint64_t value,
uint32_t addr)
  {
+   __be32 data;
int rc;
-   uint32_t data;

mutex_lock(&scom_dev->lock);

@@ -79,7 +79,7 @@ static int put_scom(struct scom_device *scom_dev, uint64_t 
value,
  static int get_scom(struct scom_device *scom_dev, uint64_t *value,
uint32_t addr)
  {
-   uint32_t result, data;
+   __be32 result, data;
int rc;


@@ -96,14 +96,13 @@ static int get_scom(struct scom_device *scom_dev, uint64_t 
*value,
if (rc)
goto bail;

-   *value |= (uint64_t)cpu_to_be32(result) << 32;
+   *value |= (uint64_t)be32_to_cpu(result) << 32;
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
sizeof(uint32_t));
if (rc)
goto bail;

-   *value |= cpu_to_be32(result);
-
+   *value |= be32_to_cpu(result);
   bail:
mutex_unlock(&scom_dev->lock);
return rc;




Re: [RFC PATCH 2/5] fsi/scom: Whitespace fixes

2018-06-13 Thread Eddie James




On 06/12/2018 12:19 AM, Benjamin Herrenschmidt wrote:

No functional changes

Signed-off-by: Benjamin Herrenschmidt 


Reviewed-by: Eddie James 


---
  drivers/fsi/fsi-scom.c | 8 
  1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index 3cba0eb645e1..8a608db0aa07 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -49,7 +49,7 @@ static struct list_head scom_devices;
  static DEFINE_IDA(scom_ida);

  static int put_scom(struct scom_device *scom_dev, uint64_t value,
-   uint32_t addr)
+   uint32_t addr)
  {
int rc;
uint32_t data;
@@ -77,7 +77,7 @@ static int put_scom(struct scom_device *scom_dev, uint64_t 
value,
  }

  static int get_scom(struct scom_device *scom_dev, uint64_t *value,
-   uint32_t addr)
+   uint32_t addr)
  {
uint32_t result, data;
int rc;
@@ -110,7 +110,7 @@ static int get_scom(struct scom_device *scom_dev, uint64_t 
*value,
  }

  static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
-   loff_t *offset)
+loff_t *offset)
  {
int rc;
struct miscdevice *mdev =
@@ -136,7 +136,7 @@ static ssize_t scom_read(struct file *filep, char __user 
*buf, size_t len,
  }

  static ssize_t scom_write(struct file *filep, const char __user *buf,
-   size_t len, loff_t *offset)
+ size_t len, loff_t *offset)
  {
int rc;
struct miscdevice *mdev = filep->private_data;




Re: [RFC PATCH 1/5] fsi/scom: Add mutex around FSI2PIB accesses

2018-06-13 Thread Eddie James




On 06/12/2018 12:19 AM, Benjamin Herrenschmidt wrote:

Otherwise, multiple clients can open the driver and attempt
to access the PIB at the same time, thus clobbering each other
in the process.

Signed-off-by: Benjamin Herrenschmidt 


Reviewed-by: Eddie James 


---
  drivers/fsi/fsi-scom.c | 25 ++---
  1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index c8eb5e5b94a7..3cba0eb645e1 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -37,6 +37,7 @@ struct scom_device {
struct list_head link;
struct fsi_device *fsi_dev;
struct miscdevice mdev;
+   struct mutex lock;
charname[32];
int idx;
  };
@@ -53,21 +54,26 @@ static int put_scom(struct scom_device *scom_dev, uint64_t 
value,
int rc;
uint32_t data;

+   mutex_lock(&scom_dev->lock);
+
data = cpu_to_be32((value >> 32) & 0x);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
sizeof(uint32_t));
if (rc)
-   return rc;
+   goto bail;

data = cpu_to_be32(value & 0x);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
sizeof(uint32_t));
if (rc)
-   return rc;
+   goto bail;

data = cpu_to_be32(SCOM_WRITE_CMD | addr);
-   return fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
+   rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
sizeof(uint32_t));
+ bail:
+   mutex_unlock(&scom_dev->lock);
+   return rc;
  }

  static int get_scom(struct scom_device *scom_dev, uint64_t *value,
@@ -76,27 +82,31 @@ static int get_scom(struct scom_device *scom_dev, uint64_t 
*value,
uint32_t result, data;
int rc;

+
+   mutex_lock(&scom_dev->lock);
*value = 0ULL;
data = cpu_to_be32(addr);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
sizeof(uint32_t));
if (rc)
-   return rc;
+   goto bail;

rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &result,
sizeof(uint32_t));
if (rc)
-   return rc;
+   goto bail;

*value |= (uint64_t)cpu_to_be32(result) << 32;
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
sizeof(uint32_t));
if (rc)
-   return rc;
+   goto bail;

*value |= cpu_to_be32(result);

-   return 0;
+ bail:
+   mutex_unlock(&scom_dev->lock);
+   return rc;
  }

  static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
@@ -183,6 +193,7 @@ static int scom_probe(struct device *dev)
if (!scom)
return -ENOMEM;

+   mutex_init(&scom->lock);
scom->idx = ida_simple_get(&scom_ida, 1, INT_MAX, GFP_KERNEL);
snprintf(scom->name, sizeof(scom->name), "scom%d", scom->idx);
scom->fsi_dev = fsi_dev;




[PATCH v10 7/7] i2c: fsi: Add bus recovery

2018-06-13 Thread Eddie James
Bus recovery should reset the engine and force clock the bus 9 times
to recover most situations.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 19 +++
 1 file changed, 19 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index d6cab4b..940b198 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -611,6 +611,24 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_BLOCK_DATA;
 }
 
+static int fsi_i2c_recover_bus(struct i2c_adapter *adap)
+{
+   int rc;
+   struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *master = port->master;
+
+   mutex_lock(&master->lock);
+
+   rc = fsi_i2c_reset(master, port->port);
+
+   mutex_unlock(&master->lock);
+   return rc;
+}
+
+static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
+   .recover_bus = fsi_i2c_recover_bus,
+};
+
 static const struct i2c_algorithm fsi_i2c_algorithm = {
.master_xfer = fsi_i2c_xfer,
.functionality = fsi_i2c_functionality,
@@ -653,6 +671,7 @@ static int fsi_i2c_probe(struct device *dev)
port->adapter.dev.of_node = np;
port->adapter.dev.parent = dev;
port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
port->adapter.algo_data = port;
 
snprintf(port->adapter.name, sizeof(port->adapter.name),
-- 
1.8.3.1



[PATCH v10 6/7] i2c: fsi: Add I2C master locking

2018-06-13 Thread Eddie James
Since there are many ports per master, each with it's own adapter and
chardev, we need some locking to prevent transfers from changing the
master state while other transfers are in progress.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 16 
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 8c0a6cb..d6cab4b 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -148,6 +149,7 @@ struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
struct list_headports;
+   struct mutexlock;
 };
 
 struct fsi_i2c_port {
@@ -574,11 +576,14 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs,
int i, rc;
unsigned long start_time;
struct fsi_i2c_port *port = adap->algo_data;
+   struct fsi_i2c_master *master = port->master;
struct i2c_msg *msg;
 
+   mutex_lock(&master->lock);
+
rc = fsi_i2c_set_port(port);
if (rc)
-   return rc;
+   goto unlock;
 
for (i = 0; i < num; i++) {
msg = msgs + i;
@@ -586,15 +591,17 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs,
 
rc = fsi_i2c_start(port, msg, i == num - 1);
if (rc)
-   return rc;
+   goto unlock;
 
rc = fsi_i2c_wait(port, msg,
  adap->timeout - (jiffies - start_time));
if (rc)
-   return rc;
+   goto unlock;
}
 
-   return num;
+unlock:
+   mutex_unlock(&master->lock);
+   return rc ? : num;
 }
 
 static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
@@ -621,6 +628,7 @@ static int fsi_i2c_probe(struct device *dev)
if (!i2c)
return -ENOMEM;
 
+   mutex_init(&i2c->lock);
i2c->fsi = to_fsi_dev(dev);
INIT_LIST_HEAD(&i2c->ports);
 
-- 
1.8.3.1



[PATCH v10 2/7] i2c: Add FSI-attached I2C master algorithm

2018-06-13 Thread Eddie James
Add register definitions for FSI-attached I2C master and functions to
access those registers over FSI. Add an FSI driver so that our I2C bus
is probed up during an FSI scan.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/Kconfig   |  11 ++
 drivers/i2c/busses/Makefile  |   1 +
 drivers/i2c/busses/i2c-fsi.c | 240 +++
 3 files changed, 252 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4f8df2e..cddd159 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1330,4 +1330,15 @@ config I2C_ZX2967
  This driver can also be built as a module. If so, the module will be
  called i2c-zx2967.
 
+config I2C_FSI
+   tristate "FSI I2C driver"
+   depends on FSI
+   help
+ Driver for FSI bus attached I2C masters. These are I2C masters that
+ are connected to the system over an FSI bus, instead of the more
+ common PCI or MMIO interface.
+
+ This driver can also be built as a module. If so, the module will be
+ called as i2c-fsi.
+
 endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5a86914..4909fd6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -137,5 +137,6 @@ obj-$(CONFIG_I2C_PCA_ISA)   += i2c-pca-isa.o
 obj-$(CONFIG_I2C_SIBYTE)   += i2c-sibyte.o
 obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
 obj-$(CONFIG_SCx200_ACB)   += scx200_acb.o
+obj-$(CONFIG_I2C_FSI)  += i2c-fsi.o
 
 ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
new file mode 100644
index 000..694bbb4
--- /dev/null
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FSI-attached I2C master algorithm
+ *
+ * Copyright 2018 IBM Corporation
+ *
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define FSI_ENGID_I2C  0x7
+
+#define I2C_DEFAULT_CLK_DIV6
+
+/* i2c registers */
+#define I2C_FSI_FIFO   0x00
+#define I2C_FSI_CMD0x04
+#define I2C_FSI_MODE   0x08
+#define I2C_FSI_WATER_MARK 0x0C
+#define I2C_FSI_INT_MASK   0x10
+#define I2C_FSI_INT_COND   0x14
+#define I2C_FSI_OR_INT_MASK0x14
+#define I2C_FSI_INTS   0x18
+#define I2C_FSI_AND_INT_MASK   0x18
+#define I2C_FSI_STAT   0x1C
+#define I2C_FSI_RESET_I2C  0x1C
+#define I2C_FSI_ESTAT  0x20
+#define I2C_FSI_RESET_ERR  0x20
+#define I2C_FSI_RESID_LEN  0x24
+#define I2C_FSI_SET_SCL0x24
+#define I2C_FSI_PORT_BUSY  0x28
+#define I2C_FSI_RESET_SCL  0x2C
+#define I2C_FSI_SET_SDA0x30
+#define I2C_FSI_RESET_SDA  0x34
+
+/* cmd register */
+#define I2C_CMD_WITH_START BIT(31)
+#define I2C_CMD_WITH_ADDR  BIT(30)
+#define I2C_CMD_RD_CONTBIT(29)
+#define I2C_CMD_WITH_STOP  BIT(28)
+#define I2C_CMD_FORCELAUNCHBIT(27)
+#define I2C_CMD_ADDR   GENMASK(23, 17)
+#define I2C_CMD_READ   BIT(16)
+#define I2C_CMD_LENGENMASK(15, 0)
+
+/* mode register */
+#define I2C_MODE_CLKDIVGENMASK(31, 16)
+#define I2C_MODE_PORT  GENMASK(15, 10)
+#define I2C_MODE_ENHANCED  BIT(3)
+#define I2C_MODE_DIAG  BIT(2)
+#define I2C_MODE_PACE_ALLOWBIT(1)
+#define I2C_MODE_WRAP  BIT(0)
+
+/* watermark register */
+#define I2C_WATERMARK_HI   GENMASK(15, 12)
+#define I2C_WATERMARK_LO   GENMASK(7, 4)
+
+#define I2C_FIFO_HI_LVL4
+#define I2C_FIFO_LO_LVL4
+
+/* interrupt register */
+#define I2C_INT_INV_CMDBIT(15)
+#define I2C_INT_PARITY BIT(14)
+#define I2C_INT_BE_OVERRUN BIT(13)
+#define I2C_INT_BE_ACCESS  BIT(12)
+#define I2C_INT_LOST_ARB   BIT(11)
+#define I2C_INT_NACK   BIT(10)
+#define I2C_INT_DAT_REQBIT(9)
+#define I2C_INT_CMD_COMP   BIT(8)
+#define I2C_INT_STOP_ERR   BIT(7)
+#define I2C_INT_BUSY   BIT(6)
+#define I2C_INT_IDLE   BIT(5)
+
+/* status register */
+#define I2C_STAT_INV_CMD   BIT(31)
+#define I2C_STAT_PARITYBIT(30)
+#define I2C_STAT_BE_OVERRUNBIT(29)
+#define I2C_STAT_BE_ACCESS BIT(28)
+#define I2C_STAT_LOST_ARB  BIT(27)
+#define I2C_STAT_NACK  BIT(26)
+#define I2C_STAT_DAT_REQ   BIT(25)
+#define I2C_STAT_CMD_COMP  BIT(24)
+#define I2C_STAT_STOP_ERR  BIT(23)
+#define I2C_STAT_MAX_PORT  GENMASK(19, 16)
+#define I2C_STAT_ANY_INT   BIT(15)
+#define I2C_STAT_SCL_INBIT(11

[PATCH v10 5/7] i2c: fsi: Add transfer implementation

2018-06-13 Thread Eddie James
Execute I2C transfers from the FSI-attached I2C master. Use polling
instead of interrupts as we have no hardware IRQ over FSI.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 195 ++-
 1 file changed, 193 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 4611a0b..8c0a6cb 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -155,6 +155,7 @@ struct fsi_i2c_port {
struct i2c_adapter  adapter;
struct fsi_i2c_master   *master;
u16 port;
+   u16 xfrd;
 };
 
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -230,6 +231,99 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
 }
 
+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
+bool stop)
+{
+   struct fsi_i2c_master *i2c = port->master;
+   u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+
+   port->xfrd = 0;
+
+   if (msg->flags & I2C_M_RD)
+   cmd |= I2C_CMD_READ;
+
+   if (stop || msg->flags & I2C_M_STOP)
+   cmd |= I2C_CMD_WITH_STOP;
+
+   cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr >> 1);
+   cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);
+
+   return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+}
+
+static int fsi_i2c_get_op_bytes(int op_bytes)
+{
+   /* fsi is limited to max 4 byte aligned ops */
+   if (op_bytes > 4)
+   return 4;
+   else if (op_bytes == 3)
+   return 2;
+   return op_bytes;
+}
+
+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+   int write;
+   int rc;
+   struct fsi_i2c_master *i2c = port->master;
+   int bytes_to_write = i2c->fifo_size - fifo_count;
+   int bytes_remaining = msg->len - port->xfrd;
+
+   bytes_to_write = min(bytes_to_write, bytes_remaining);
+
+   while (bytes_to_write) {
+   write = fsi_i2c_get_op_bytes(bytes_to_write);
+
+   rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
+ &msg->buf[port->xfrd], write);
+   if (rc)
+   return rc;
+
+   port->xfrd += write;
+   bytes_to_write -= write;
+   }
+
+   return 0;
+}
+
+static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+u8 fifo_count)
+{
+   int read;
+   int rc;
+   struct fsi_i2c_master *i2c = port->master;
+   int bytes_to_read;
+   int xfr_remaining = msg->len - port->xfrd;
+   u32 dummy;
+
+   bytes_to_read = min_t(int, fifo_count, xfr_remaining);
+
+   while (bytes_to_read) {
+   read = fsi_i2c_get_op_bytes(bytes_to_read);
+
+   if (xfr_remaining) {
+   rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
+&msg->buf[port->xfrd], read);
+   if (rc)
+   return rc;
+
+   port->xfrd += read;
+   xfr_remaining -= read;
+   } else {
+   /* no more buffer but data in fifo, need to clear it */
+   rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
+read);
+   if (rc)
+   return rc;
+   }
+
+   bytes_to_read -= read;
+   }
+
+   return 0;
+}
+
 static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c)
 {
int i, rc;
@@ -393,17 +487,114 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 
status)
return -ETIMEDOUT;
 }
 
+static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
+struct i2c_msg *msg, u32 status)
+{
+   int rc;
+   u8 fifo_count;
+
+   if (status & I2C_STAT_ERR) {
+   rc = fsi_i2c_abort(port, status);
+   if (rc)
+   return rc;
+
+   if (status & I2C_STAT_INV_CMD)
+   return -EINVAL;
+
+   if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
+   I2C_STAT_BE_ACCESS))
+   return -EPROTO;
+
+   if (status & I2C_STAT_NACK)
+   return -ENXIO;
+
+   if (status & I2C_STAT_LOST_ARB)
+   return -EAGAIN;
+
+   if (status & I2C_STAT_STOP_ERR)
+   return -EBADMSG;
+
+   return -EIO;
+   }
+
+   if (status & I2C_STAT_DAT_REQ) {
+ 

[PATCH v10 1/7] dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation

2018-06-13 Thread Eddie James
Document the bindings.

Signed-off-by: Eddie James >
Acked-by: Rob Herring 
---
 Documentation/devicetree/bindings/i2c/i2c-fsi.txt | 40 +++
 1 file changed, 40 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-fsi.txt 
b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
new file mode 100644
index 000..b1be2ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
@@ -0,0 +1,40 @@
+Device-tree bindings for FSI-attached I2C master and busses
+---
+
+Required properties:
+ - compatible = "ibm,i2c-fsi";
+ - reg = < address size >; : The FSI CFAM address and address
+ space size.
+ - #address-cells = <1>;   : Number of address cells in child
+ nodes.
+ - #size-cells = <0>;  : Number of size cells in child nodes.
+ - child nodes : Nodes to describe busses off the I2C
+ master.
+
+Child node required properties:
+ - reg = < port number >   : The port number on the I2C master.
+
+Child node optional properties:
+ - child nodes : Nodes to describe devices on the I2C
+ bus.
+
+Examples:
+
+i2c@1800 {
+compatible = "ibm,i2c-fsi";
+reg = < 0x1800 0x400 >;
+#address-cells = <1>;
+#size-cells = <0>;
+
+i2c-bus@0 {
+reg = <0>;
+};
+
+i2c-bus@1 {
+reg = <1>;
+
+eeprom@50 {
+compatible = "vendor,dev-name";
+};
+};
+};
-- 
1.8.3.1



[PATCH v10 4/7] i2c: fsi: Add abort and hardware reset procedures

2018-06-13 Thread Eddie James
Add abort procedure for failed transfers. Add engine and bus reset
procedures to recover from as many faults as possible.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 179 +++
 1 file changed, 179 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 695818f..4611a0b 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -12,10 +12,12 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -128,6 +130,20 @@
 #define I2C_ESTAT_SELF_BUSYBIT(6)
 #define I2C_ESTAT_VERSION  GENMASK(4, 0)
 
+/* port busy register */
+#define I2C_PORT_BUSY_RESETBIT(31)
+
+/* wait for command complete or data request */
+#define I2C_CMD_SLEEP_MAX_US   500
+#define I2C_CMD_SLEEP_MIN_US   50
+
+/* wait after reset; choose time from legacy driver */
+#define I2C_RESET_SLEEP_MAX_US 2000
+#define I2C_RESET_SLEEP_MIN_US 1000
+
+/* choose timeout length from legacy driver; it's well tested */
+#define I2C_ABORT_TIMEOUT  msecs_to_jiffies(100)
+
 struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
@@ -214,6 +230,169 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
 }
 
+static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c)
+{
+   int i, rc;
+   u32 mode, stat, ext, dummy = 0;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   mode |= I2C_MODE_DIAG;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   for (i = 0; i < 9; i++) {
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+   if (rc)
+   return rc;
+   }
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
+   if (rc)
+   return rc;
+
+   mode &= ~I2C_MODE_DIAG;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+   if (rc)
+   return rc;
+
+   /* check for hardware fault */
+   if (!(stat & I2C_STAT_SCL_IN) || !(stat & I2C_STAT_SDA_IN)) {
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &ext);
+   if (rc)
+   return rc;
+
+   dev_err(&i2c->fsi->dev, "bus stuck status[%08X] ext[%08X]\n",
+   stat, ext);
+   }
+
+   return 0;
+}
+
+static int fsi_i2c_reset(struct fsi_i2c_master *i2c, u16 port)
+{
+   int rc;
+   u32 mode, stat, dummy = 0;
+
+   /* reset engine */
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+   if (rc)
+   return rc;
+
+   /* re-init engine */
+   rc = fsi_i2c_dev_init(i2c);
+   if (rc)
+   return rc;
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   /* set port; default after reset is 0 */
+   if (port) {
+   mode &= ~I2C_MODE_PORT;
+   mode |= FIELD_PREP(I2C_MODE_PORT, port);
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+   }
+
+   /* reset busy register; hw workaround */
+   dummy = I2C_PORT_BUSY_RESET;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy);
+   if (rc)
+   return rc;
+
+   /* force bus reset */
+   rc = fsi_i2c_reset_bus(i2c);
+   if (rc)
+   return rc;
+
+   /* reset errors */
+   dummy = 0;
+   rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+   if (rc)
+   return rc;
+
+   /* wait for command complete */
+   usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US);
+
+   rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+   if (rc)
+   return rc;
+
+   if (stat & I2C_STAT_CMD_COMP)
+   return rc;
+
+   /* failed to get command complete; reset engine again */
+   rc = fsi_i2c_write_reg(i2c->fsi, I2

[PATCH v10 0/7] i2c: Add FSI-attached I2C master algorithm

2018-06-13 Thread Eddie James
This series adds an algorithm for an I2C master physically located on an FSI
slave device. The I2C master has multiple ports, each of which may be connected
to an I2C slave. Access to the I2C master registers is achieved over FSI bus.

Due to the multi-port nature of the I2C master, the driver instantiates a new
I2C adapter for each port connected to a slave. The connected ports should be
defined in the device tree under the I2C master device.

Changes since v9
 - Switch the status masks to use a combination of bits rather than directly
   coding the value
 - Remove the interrupt mask definition as it was unused
 - Fixed return value of master_xfer function (return number of xfrs, not 0)

Changes since v8
 - Drop unecessary else statements
 - Use i++ instead of ++i
 - Use kzalloc/kfree instead of devm_kzalloc/devm_kfree for port structure
 - Drop the list_empty check in remove

Changes since v7
 - Fix grammer in Kconfig (a -> an)
 - Change I2C registers to use BIT and GENMASK
 - Remove custom macros and use FIELD_PREP and FIELD_GET
 - Fix a few unecessary initializations and "return rc" that are always zero
 - Clean up the read/write fifo functions a bit
 - Few other clean-up items

Changes since v6
 - Remove spinlock for reset functionality; it's unecessary and doesn't work
   with the latest FSI core.
 - Use a mutex instead of a semaphore, and don't wait for timeout to get the
   lock.
 - Use usleeps instead of schedule_timeout; it's not worth the overhead when
   the wait should be very short in between sending the command and receiving
   the response.

Changes since v5
 - Fix reset functionality and do a reset after every transfer failure

Eddie James (7):
  dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation
  i2c: Add FSI-attached I2C master algorithm
  i2c: fsi: Add port structures
  i2c: fsi: Add abort and hardware reset procedures
  i2c: fsi: Add transfer implementation
  i2c: fsi: Add I2C master locking
  i2c: fsi: Add bus recovery

 Documentation/devicetree/bindings/i2c/i2c-fsi.txt |  40 ++
 drivers/i2c/busses/Kconfig|  11 +
 drivers/i2c/busses/Makefile   |   1 +
 drivers/i2c/busses/i2c-fsi.c  | 727 ++
 4 files changed, 779 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

-- 
1.8.3.1



[PATCH v10 3/7] i2c: fsi: Add port structures

2018-06-13 Thread Eddie James
Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports for each master are defined in the devicetree.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 90 
 1 file changed, 90 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 694bbb4..695818f 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -17,7 +17,10 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #define FSI_ENGID_I2C  0x7
 
@@ -128,6 +131,14 @@
 struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
+   struct list_headports;
+};
+
+struct fsi_i2c_port {
+   struct list_headlist;
+   struct i2c_adapter  adapter;
+   struct fsi_i2c_master   *master;
+   u16 port;
 };
 
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -181,9 +192,38 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
 }
 
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+   int rc;
+   struct fsi_device *fsi = port->master->fsi;
+   u32 mode, dummy = 0;
+
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   if (FIELD_GET(I2C_MODE_PORT, mode) == port->port)
+   return 0;
+
+   mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
+   rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   /* reset engine when port is changed */
+   return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
 {
+   int rc;
+   struct fsi_i2c_port *port = adap->algo_data;
+
+   rc = fsi_i2c_set_port(port);
+   if (rc)
+   return rc;
+
return -EOPNOTSUPP;
 }
 
@@ -202,23 +242,72 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
 static int fsi_i2c_probe(struct device *dev)
 {
struct fsi_i2c_master *i2c;
+   struct fsi_i2c_port *port;
+   struct device_node *np;
int rc;
+   u32 port_no;
 
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
 
i2c->fsi = to_fsi_dev(dev);
+   INIT_LIST_HEAD(&i2c->ports);
 
rc = fsi_i2c_dev_init(i2c);
if (rc)
return rc;
 
+   /* Add adapter for each i2c port of the master. */
+   for_each_available_child_of_node(dev->of_node, np) {
+   rc = of_property_read_u32(np, "reg", &port_no);
+   if (rc || port_no > USHRT_MAX)
+   continue;
+
+   port = kzalloc(sizeof(*port), GFP_KERNEL);
+   if (!port)
+   break;
+
+   port->master = i2c;
+   port->port = port_no;
+
+   port->adapter.owner = THIS_MODULE;
+   port->adapter.dev.of_node = np;
+   port->adapter.dev.parent = dev;
+   port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.algo_data = port;
+
+   snprintf(port->adapter.name, sizeof(port->adapter.name),
+"i2c_bus-%u", port_no);
+
+   rc = i2c_add_adapter(&port->adapter);
+   if (rc < 0) {
+   dev_err(dev, "Failed to register adapter: %d\n", rc);
+   kfree(port);
+   continue;
+   }
+
+   list_add(&port->list, &i2c->ports);
+   }
+
dev_set_drvdata(dev, i2c);
 
return 0;
 }
 
+static int fsi_i2c_remove(struct device *dev)
+{
+   struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+   struct fsi_i2c_port *port;
+
+   list_for_each_entry(port, &i2c->ports, list) {
+   i2c_del_adapter(&port->adapter);
+   kfree(port);
+   }
+
+   return 0;
+}
+
 static const struct fsi_device_id fsi_i2c_ids[] = {
{ FSI_ENGID_I2C, FSI_VERSION_ANY },
{ }
@@ -230,6 +319,7 @@ static int fsi_i2c_probe(struct device *dev)
.name = "i2c-fsi",
.bus = &fsi_bus_type,
.probe = fsi_i2c_probe,
+   .remove = fsi_i2c_remove,
},
 };
 
-- 
1.8.3.1



Re: [RFC PATCH 5/5] fsi/scom: Major overhaul

2018-06-14 Thread Eddie James




On 06/13/2018 06:00 PM, Benjamin Herrenschmidt wrote:

On Wed, 2018-06-13 at 09:57 -0500, Eddie James wrote:

On 06/12/2018 12:19 AM, Benjamin Herrenschmidt wrote:

This was too hard to split ... this adds a number of features
to the SCOM user interface:

   - Support for indirect SCOMs

   - read()/write() interface now handle errors and retries

   - New ioctl() "raw" interface for use by debuggers

Signed-off-by: Benjamin Herrenschmidt 
---
   drivers/fsi/fsi-scom.c   | 424 ---
   include/uapi/linux/fsi.h |  56 ++
   2 files changed, 450 insertions(+), 30 deletions(-)
   create mode 100644 include/uapi/linux/fsi.h

diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index e98573ecdae1..39c74351f1bf 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -24,6 +24,8 @@
   #include 
   #include 
+
+static int scom_reset(struct scom_device *scom, void __user *argp)
+{
+   uint32_t flags, dummy = -1;
+   int rc = 0;
+
+   if (get_user(flags, (__u32 __user *)argp))
+   return -EFAULT;
+   if (flags & SCOM_RESET_PIB)
+   rc = fsi_device_write(scom->fsi_dev, SCOM_PIB_RESET_REG, &dummy,
+ sizeof(uint32_t));

I realize this is a user requested flag but I believe the BMC is never
supposed to issue this type of reset, due to the possibility of breaking
stuff on the host side. Not sure if it should even be available?

It's for use by cronus or similar low level system debuggers.

Cheers,
Ben.


Cool, thanks.

Reviewed-by: Eddie James 




Otherwise, looks good!
Thanks,
Eddie


+   if (!rc && (flags & (SCOM_RESET_PIB | SCOM_RESET_INTF)))
+   rc = fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, 
&dummy,
+ sizeof(uint32_t));
+   return rc;
+}
+
+static int scom_check(struct scom_device *scom, void __user *argp)
+{
+   /* Still need to find out how to get "protected" */
+   return put_user(SCOM_CHECK_SUPPORTED, (__u32 __user *)argp);
+}
+
+static long scom_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+   struct miscdevice *mdev = file->private_data;
+   struct scom_device *scom = to_scom_dev(mdev);
+   void __user *argp = (void __user *)arg;
+   int rc = -ENOTTY;
+
+   mutex_lock(&scom->lock);
+   switch(cmd) {
+   case FSI_SCOM_CHECK:
+   rc = scom_check(scom, argp);
+   break;
+   case FSI_SCOM_READ:
+   rc = scom_raw_read(scom, argp);
+   break;
+   case FSI_SCOM_WRITE:
+   rc = scom_raw_write(scom, argp);
+   break;
+   case FSI_SCOM_RESET:
+   rc = scom_reset(scom, argp);
+   break;
+   }
+   mutex_unlock(&scom->lock);
+   return rc;
+}
+
   static const struct file_operations scom_fops = {
-   .owner  = THIS_MODULE,
-   .llseek = scom_llseek,
-   .read   = scom_read,
-   .write  = scom_write,
+   .owner  = THIS_MODULE,
+   .llseek = scom_llseek,
+   .read   = scom_read,
+   .write  = scom_write,
+   .unlocked_ioctl = scom_ioctl,
   };

   static int scom_probe(struct device *dev)
diff --git a/include/uapi/linux/fsi.h b/include/uapi/linux/fsi.h
new file mode 100644
index ..6008d93f2e48
--- /dev/null
+++ b/include/uapi/linux/fsi.h
@@ -0,0 +1,56 @@
+#ifndef _UAPI_LINUX_FSI_H
+#define _UAPI_LINUX_FSI_H
+
+#include 
+
+/*
+ * /dev/scom "raw" ioctl interface
+ *
+ * The driver supports a high level "read/write" interface which
+ * handles retries and converts the status to Linux error codes,
+ * however low level tools an debugger need to access the "raw"
+ * HW status information and interpret it themselves, so this
+ * ioctl interface is also provided for their use case.
+ */
+
+/* Structure for SCOM read/write */
+struct scom_access {
+   __u64   addr;   /* SCOM address, supports indirect */
+   __u64   data;   /* SCOM data (in for write, out for read) */
+   __u64   mask;   /* Data mask for writes */
+   __u32   intf_errors;/* Interface error flags */
+#define SCOM_INTF_ERR_PARITY   0x0001 /* Parity error */
+#define SCOM_INTF_ERR_PROTECTION   0x0002 /* Blocked by secure boot */
+#define SCOM_INTF_ERR_ABORT0x0004 /* PIB reset during access */
+#define SCOM_INTF_ERR_UNKNOWN  0x8000 /* Unknown error */
+   /*
+* Note: Any other bit set in intf_errors need to be considered as an
+* error. Future implementations may define new error conditions. The
+* pib_status below is only valid if intf_errors is 0.
+*/
+   __u8pib_status; /* 3-bit PIB status */
+#define SCOM_PIB_SUCCESS   0   /* Access successful */
+#define SCOM_PIB

Re: [PATCH v8 2/2] media: platform: Add Aspeed Video Engine driver

2018-12-14 Thread Eddie James




On 12/13/2018 07:09 PM, Joel Stanley wrote:

On Wed, 12 Dec 2018 at 04:09, Eddie James  wrote:

The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting a service processor, the Video Engine can capture
the host processor graphics output.
+ASPEED VIDEO ENGINE DRIVER
+M: Eddie James 
+L: linux-me...@vger.kernel.org
+L: open...@lists.ozlabs.org (moderated for non-subscribers)

We tend to use the linux-aspeed list for upstream kernel discussions.
Up to you if you want to use the openbmc list though.


  source "drivers/media/platform/omap/Kconfig"

+config VIDEO_ASPEED
+   tristate "Aspeed AST2400 and AST2500 Video Engine driver"
+   depends on VIDEO_V4L2
+   select VIDEOBUF2_DMA_CONTIG
+   help
+ Support for the Aspeed Video Engine (VE) embedded in the Aspeed
+ AST2400 and AST2500 SOCs. The VE can capture and compress video data
+ from digital or analog sources.

This might need updating in response to my questions below about
ast2400 testing.


+++ b/drivers/media/platform/aspeed-video.c
@@ -0,0 +1,1729 @@
+// SPDX-License-Identifier: GPL-2.0+

You need to put this there as well:

// Copyright 2018 IBM Corp



+static int aspeed_video_init(struct aspeed_video *video)
+{
+   int irq;
+   int rc;
+   struct device *dev = video->dev;
+
+   irq = irq_of_parse_and_map(dev->of_node, 0);
+   if (!irq) {
+   dev_err(dev, "Unable to find IRQ\n");
+   return -ENODEV;
+   }
+
+   rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED,

The datasheet indicates this IRQ is for the video engline only, so I
don't think you want IRQF_SHARED.


+ DEVICE_NAME, video);
+   if (rc < 0) {
+   dev_err(dev, "Unable to request IRQ %d\n", irq);
+   return rc;
+   }
+
+   video->eclk = devm_clk_get(dev, "eclk");
+   if (IS_ERR(video->eclk)) {
+   dev_err(dev, "Unable to get ECLK\n");
+   return PTR_ERR(video->eclk);
+   }
+
+   video->vclk = devm_clk_get(dev, "vclk");
+   if (IS_ERR(video->vclk)) {
+   dev_err(dev, "Unable to get VCLK\n");
+   return PTR_ERR(video->vclk);
+   }
+
+   video->rst = devm_reset_control_get_exclusive(dev, NULL);
+   if (IS_ERR(video->rst)) {
+   dev_err(dev, "Unable to get VE reset\n");
+   return PTR_ERR(video->rst);
+   }

As discussed in the clock driver, this can go as you've already
released the reset when enabling the eclk.


I use the reset control by itself during a resolution change, so I would 
like to have it available.




However, you're requesting the clock without enabling it. You need to
do a clk_prepare_enable().


That's because the clock is only enabled when the device is opened. No 
reason to turn it on at boot time.





+
+   rc = of_reserved_mem_device_init(dev);
+   if (rc) {
+   dev_err(dev, "Unable to reserve memory\n");
+   return rc;
+   }
+
+   rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+   if (rc) {
+   dev_err(dev, "Failed to set DMA mask\n");
+   of_reserved_mem_device_release(dev);
+   return rc;
+   }
+
+   if (!aspeed_video_alloc_buf(video, &video->jpeg,
+   VE_JPEG_HEADER_SIZE)) {
+   dev_err(dev, "Failed to allocate DMA for JPEG header\n");
+   of_reserved_mem_device_release(dev);
+   return rc;
+   }
+
+   aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420);
+
+   return 0;
+}
+
+static const struct of_device_id aspeed_video_of_match[] = {
+   { .compatible = "aspeed,ast2400-video-engine" },

I noticed the clock driver did not have the changed required for the
2400. Have you tested this on the ast2400?


The clocking setup is different on the ast2400. Xiuzhi on the openbmc 
list has been testing on the ast2400 successfully.


Thanks,
Eddie





+   { .compatible = "aspeed,ast2500-video-engine" },
+   {}
+};
+MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
+
+static struct platform_driver aspeed_video_driver = {
+   .driver = {
+   .name = DEVICE_NAME,
+   .of_match_table = aspeed_video_of_match,
+   },
+   .probe = aspeed_video_probe,
+   .remove = aspeed_video_remove,
+};




Re: [PATCH 2/2] clk: aspeed: Setup video engine clocking

2018-12-14 Thread Eddie James




On 12/13/2018 07:02 PM, Joel Stanley wrote:

Hi Eddie,

On Wed, 12 Dec 2018 at 06:42, Eddie James  wrote:

Add the video engine reset bit. Add eclk mux and clock divider table.

Signed-off-by: Eddie James 
Acked-by: Stephen Boyd 
---
  drivers/clk/clk-aspeed.c | 41 +++--
  1 file changed, 39 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 5961367..f16ce7d 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -87,7 +87,7 @@ struct aspeed_clk_gate {
  /* TODO: ask Aspeed about the actual parent data */
  static const struct aspeed_gate_data aspeed_gates[] = {
 /*   clk rst   name parent 
 flags */
-   [ASPEED_CLK_GATE_ECLK] ={  0, -1, "eclk-gate",  "eclk", 
0 }, /* Video Engine */
+   [ASPEED_CLK_GATE_ECLK] ={  0,  6, "eclk-gate",  "eclk", 
0 }, /* Video Engine */



 [ASPEED_CLK_GATE_GCLK] ={  1,  7, "gclk-gate",  NULL,  
 0 }, /* 2D engine */
 [ASPEED_CLK_GATE_MCLK] ={  2, -1, "mclk-gate",  
"mpll", CLK_IS_CRITICAL }, /* SDRAM */
 [ASPEED_CLK_GATE_VCLK] ={  3,  6, "vclk-gate",  NULL,  
 0 }, /* Video Capture */
@@ -113,6 +113,24 @@ struct aspeed_clk_gate {
 [ASPEED_CLK_GATE_LHCCLK] =  { 28, -1, "lhclk-gate", 
"lhclk", 0 }, /* LPC master/LPC+ */
  };

+static const char * const eclk_parent_names[] = {
+   "mpll",

Is the mpll really an input to the eclk?


Yep, it's the default.




+   "hpll",
+   "dpll",

We don't have a dpll in the driver that I can see.


True... I supposed I would just add the parent here now in case the dpll 
clock is ever added.





+};
+
+static const struct clk_div_table ast2500_eclk_div_table[] = {

Is the clocking setup different on the ast2400?


Yes, the dividers are the default ast2400 ones.




+   { 0x0, 2 },
+   { 0x1, 2 },
+   { 0x2, 3 },
+   { 0x3, 4 },
+   { 0x4, 5 },
+   { 0x5, 6 },
+   { 0x6, 7 },
+   { 0x7, 8 },
+   { 0 }
+};
@@ -317,6 +338,7 @@ struct aspeed_reset {
 [ASPEED_RESET_PECI] = 10,
 [ASPEED_RESET_I2C]  =  2,
 [ASPEED_RESET_AHB]  =  1,
+   [ASPEED_RESET_VIDEO]=  6,

You've added the reset line to the ASPEED_CLK_GATE_ECLK clock so you
do not need to separately expose the reset controller. Instead
enabling the clock will deassert the rest line for you.

This means you should drop the change from the header too, and it
affects the bindings document for the video engine.


I want that reset available separately for use in the video engine 
actually. I could do without it, but it's somewhat useful.


Thanks,
Eddie




 /*
  * SCUD4 resets start at an offset to separate them from
@@ -522,6 +544,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
 return PTR_ERR(hw);
 aspeed_clk_data->hws[ASPEED_CLK_24M] = hw;

+   hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names,
+ARRAY_SIZE(eclk_parent_names), 0,
+scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0,
+&aspeed_clk_lock);
+   if (IS_ERR(hw))
+   return PTR_ERR(hw);
+   aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw;
+
+   hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0,
+  scu_base + ASPEED_CLK_SELECTION, 28,
+  3, 0, soc_data->eclk_div_table,
+  &aspeed_clk_lock);
+   if (IS_ERR(hw))
+   return PTR_ERR(hw);
+   aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw;
+
 /*
  * TODO: There are a number of clocks that not included in this driver
  * as more information is required:
@@ -531,7 +569,6 @@ static int aspeed_clk_probe(struct platform_device *pdev)
  *   RGMII
  *   RMII
  *   UART[1..5] clock source mux
-*   Video Engine (ECLK) mux and clock divider
  */

 for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
--
1.8.3.1





[PATCH v7 2/2] media: platform: Add Aspeed Video Engine driver

2018-12-10 Thread Eddie James
The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting a service processor, the Video Engine can capture
the host processor graphics output.

Add a V4L2 driver to capture video data and compress it to JPEG images.
Make the video frames available through the V4L2 streaming interface.

Signed-off-by: Eddie James 
---
 MAINTAINERS   |8 +
 drivers/media/platform/Kconfig|9 +
 drivers/media/platform/Makefile   |1 +
 drivers/media/platform/aspeed-video.c | 1717 +
 4 files changed, 1735 insertions(+)
 create mode 100644 drivers/media/platform/aspeed-video.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7f5c634..136ffb0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2426,6 +2426,14 @@ S:   Maintained
 F: Documentation/hwmon/asc7621
 F: drivers/hwmon/asc7621.c
 
+ASPEED VIDEO ENGINE DRIVER
+M: Eddie James 
+L: linux-me...@vger.kernel.org
+L: open...@lists.ozlabs.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/media/platform/aspeed-video.c
+F: Documentation/devicetree/bindings/media/aspeed-video.txt
+
 ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
 M: Corentin Chary 
 L: acpi4asus-u...@lists.sourceforge.net
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ea33063..a505e9f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -32,6 +32,15 @@ source "drivers/media/platform/davinci/Kconfig"
 
 source "drivers/media/platform/omap/Kconfig"
 
+config VIDEO_ASPEED
+   tristate "Aspeed AST2400 and AST2500 Video Engine driver"
+   depends on VIDEO_V4L2
+   select VIDEOBUF2_DMA_CONTIG
+   help
+ Support for the Aspeed Video Engine (VE) embedded in the Aspeed
+ AST2400 and AST2500 SOCs. The VE can capture and compress video data
+ from digital or analog sources.
+
 config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index d347a55..e6deb25 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the video capture/playback device drivers.
 #
 
+obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o
 obj-$(CONFIG_VIDEO_CADENCE)+= cadence/
 obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
 obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
diff --git a/drivers/media/platform/aspeed-video.c 
b/drivers/media/platform/aspeed-video.c
new file mode 100644
index 000..c24acb9
--- /dev/null
+++ b/drivers/media/platform/aspeed-video.c
@@ -0,0 +1,1717 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DEVICE_NAME"aspeed-video"
+
+#define ASPEED_VIDEO_JPEG_NUM_QUALITIES12
+#define ASPEED_VIDEO_JPEG_HEADER_SIZE  10
+#define ASPEED_VIDEO_JPEG_QUANT_SIZE   116
+#define ASPEED_VIDEO_JPEG_DCT_SIZE 34
+
+#define MAX_FRAME_RATE 60
+#define MAX_HEIGHT 1200
+#define MAX_WIDTH  1920
+#define MIN_HEIGHT 480
+#define MIN_WIDTH  640
+
+#define NUM_POLARITY_CHECKS10
+#define INVALID_RESOLUTION_RETRIES 2
+#define INVALID_RESOLUTION_DELAY   msecs_to_jiffies(250)
+#define RESOLUTION_CHANGE_DELAYmsecs_to_jiffies(500)
+#define MODE_DETECT_TIMEOUTmsecs_to_jiffies(500)
+#define STOP_TIMEOUT   msecs_to_jiffies(1000)
+#define DIRECT_FETCH_THRESHOLD 0x0c /* 1024 * 768 */
+
+#define VE_MAX_SRC_BUFFER_SIZE 0x8ca000 /* 1920 * 1200, 32bpp */
+#define VE_JPEG_HEADER_SIZE0x006000 /* 512 * 12 * 4 */
+
+#define VE_PROTECTION_KEY  0x000
+#define  VE_PROTECTION_KEY_UNLOCK  0x1a038aa8
+
+#define VE_SEQ_CTRL0x004
+#define  VE_SEQ_CTRL_TRIG_MODE_DET BIT(0)
+#define  VE_SEQ_CTRL_TRIG_CAPTURE  BIT(1)
+#define  VE_SEQ_CTRL_FORCE_IDLEBIT(2)
+#define  VE_SEQ_CTRL_MULT_FRAMEBIT(3)
+#define  VE_SEQ_CTRL_TRIG_COMP BIT(4)
+#define  VE_SEQ_CTRL_AUTO_COMP BIT(5)
+#define  VE_SEQ_CTRL_EN_WATCHDOG   BIT(7)
+#define  VE_SEQ_CTRL_YUV420BIT(10)
+#define  VE_SEQ_CTRL_COMP_FMT  GENMASK(11, 10)
+#define  VE_SEQ_CTRL_HALT  BIT(12)
+#define  VE_SEQ_CTRL_EN_WATCHDOG_COMP  BIT(14)
+#define  VE_SEQ_CTRL_TRIG_JPG  BIT(15)
+#defi

[PATCH v7 0/2] media: platform: Add Aspeed Video Engine driver

2018-12-10 Thread Eddie James
From: Eddie James 

The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting as a service processor, the Video Engine can
capture the host processor graphics output.

This series adds a V4L2 driver for the VE, providing the usual V4L2 streaming
interface by way of videobuf2. Each frame, the driver triggers the hardware to
capture the host graphics output and compress it to JPEG format.

v4l2-compliance SHA: d039b47a108596ca004b11e52989054882d45888, 32 bits

Compliance test for device /dev/video0:

Driver Info:
Driver name  : aspeed-video
Card type: Aspeed Video Engine
Bus info : platform:aspeed-video
Driver version   : 4.19.6
Capabilities : 0x8521
Video Capture
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps  : 0x0521
Video Capture
Read/Write
Streaming
Extended Pix Format

Required ioctls:
test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK

Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
test VIDIOC_DV_TIMINGS_CAP: OK
test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
warn: v4l2-test-controls.cpp(853): V4L2_CID_DV_RX_POWER_PRESENT 
not found for input 0
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 3 Private Controls: 0

Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)

Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)

Test input 0:

Streaming ioctls:
test read/write: OK
test blocking wait: OK
test MMAP: OK 
test USERPTR: OK (Not Supported)
test DMABUF: Cannot test, specify --expbuf-device

Total: 48, Succeeded: 48, Failed: 0, Warnings: 1

I'm unable to run linux-next or the media tree as our system doesn't boot
without quite a few patches that aren't yet upstreamed, so I can't grab the
fix for that warning Hans mentioned.

Changes since v6:
 - Refactor open/start process such that the file handle can be opened without
   a video signal present
 - Added standards and capabilities
 - Removed used of VB2_BUF_FLAG_LAST
 - Added a few comments
 - Removed a couple of functions that were only used once
 - Removed client counter

Changes since v5:
 - Rework resolution change and v4l2 timings functions again with active and
   detected timings.
 - Renamed a few things.
 - Fixed polarities in the timings.

Changes since v4:
 - Set real min and max resolution in enum_dv_timings
 - Add check for vb2_is_busy before settings the timings
 - Set max frame rate to the actual max rather than max + 1
 - Correct the input status to 0.
 - Rework resolution change to only set the relevant h/w regs during startup or
   when set_timings is called.

Changes

[PATCH v7 1/2] dt-bindings: media: Add Aspeed Video Engine binding documentation

2018-12-10 Thread Eddie James
Document the bindings.

Signed-off-by: Eddie James 
Reviewed-by: Rob Herring 
---
 .../devicetree/bindings/media/aspeed-video.txt | 26 ++
 1 file changed, 26 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt

diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt 
b/Documentation/devicetree/bindings/media/aspeed-video.txt
new file mode 100644
index 000..78b464a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/aspeed-video.txt
@@ -0,0 +1,26 @@
+* Device tree bindings for Aspeed Video Engine
+
+The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can
+capture and compress video data from digital or analog sources.
+
+Required properties:
+ - compatible: "aspeed,ast2400-video-engine" or
+   "aspeed,ast2500-video-engine"
+ - reg:contains the offset and length of the VE memory 
region
+ - clocks: clock specifiers for the syscon clocks associated with
+   the VE (ordering must match the clock-names property)
+ - clock-names:"vclk" and "eclk"
+ - resets: reset specifier for the syscon reset associated with
+   the VE
+ - interrupts: the interrupt associated with the VE on this platform
+
+Example:
+
+video-engine@1e70 {
+compatible = "aspeed,ast2500-video-engine";
+reg = <0x1e70 0x2>;
+clocks = <&syscon ASPEED_CLK_GATE_VCLK>, <&syscon ASPEED_CLK_GATE_ECLK>;
+clock-names = "vclk", "eclk";
+resets = <&syscon ASPEED_RESET_VIDEO>;
+interrupts = <7>;
+};
-- 
1.8.3.1



Re: [PATCH v7 2/2] media: platform: Add Aspeed Video Engine driver

2018-12-11 Thread Eddie James




On 12/11/2018 04:44 AM, Hans Verkuil wrote:

On 12/10/18 11:26 PM, Eddie James wrote:

The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting a service processor, the Video Engine can capture
the host processor graphics output.

Add a V4L2 driver to capture video data and compress it to JPEG images.
Make the video frames available through the V4L2 streaming interface.

Signed-off-by: Eddie James 
---




+static void aspeed_video_irq_res_change(struct aspeed_video *video)
+{
+   dev_dbg(video->dev, "Resolution changed; resetting\n");
+
+   set_bit(VIDEO_RES_CHANGE, &video->flags);
+   clear_bit(VIDEO_FRAME_INPRG, &video->flags);
+
+   aspeed_video_off(video);
+   aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR);
+
+   schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY);
+}
+
+static irqreturn_t aspeed_video_irq(int irq, void *arg)
+{
+   struct aspeed_video *video = arg;
+   u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS);
+
+   /* Resolution changed; reset entire engine and reinitialize */
+   if (sts & VE_INTERRUPT_MODE_DETECT_WD) {

Does this only trigger when the resolution changed, or also when the signal
disappears? For the purpose of this review I assume that it is also triggered
when the signal goes away. The comment should be updated in that case that
it is not just resolution changes, but also a dropped video signal.


That's correct, signal lost or resolution changed, I'll update the comment.




+   aspeed_video_irq_res_change(video);
+   return IRQ_HANDLED;
+   }
+
+   if (sts & VE_INTERRUPT_MODE_DETECT) {
+   if (test_bit(VIDEO_RES_DETECT, &video->flags)) {
+   aspeed_video_update(video, VE_INTERRUPT_CTRL,
+   VE_INTERRUPT_MODE_DETECT, 0);
+   aspeed_video_write(video, VE_INTERRUPT_STATUS,
+  VE_INTERRUPT_MODE_DETECT);
+
+   set_bit(VIDEO_MODE_DETECT_DONE, &video->flags);
+   wake_up_interruptible_all(&video->wait);
+   } else {
+   aspeed_video_irq_res_change(video);
+   return IRQ_HANDLED;
+   }
+   }
+
+   if ((sts & VE_INTERRUPT_COMP_COMPLETE) &&
+   (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) {
+   struct aspeed_video_buffer *buf;
+   u32 frame_size = aspeed_video_read(video,
+  VE_OFFSET_COMP_STREAM);
+
+   spin_lock(&video->lock);
+   clear_bit(VIDEO_FRAME_INPRG, &video->flags);
+   buf = list_first_entry_or_null(&video->buffers,
+  struct aspeed_video_buffer,
+  link);
+   if (buf) {
+   vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size);
+
+   if (!list_is_last(&buf->link, &video->buffers)) {
+   buf->vb.vb2_buf.timestamp = ktime_get_ns();
+   buf->vb.sequence = video->sequence++;
+   buf->vb.field = V4L2_FIELD_NONE;
+   vb2_buffer_done(&buf->vb.vb2_buf,
+   VB2_BUF_STATE_DONE);
+   list_del(&buf->link);
+   }
+   }
+   spin_unlock(&video->lock);
+
+   aspeed_video_update(video, VE_SEQ_CTRL,
+   VE_SEQ_CTRL_TRIG_CAPTURE |
+   VE_SEQ_CTRL_FORCE_IDLE |
+   VE_SEQ_CTRL_TRIG_COMP, 0);
+   aspeed_video_update(video, VE_INTERRUPT_CTRL,
+   VE_INTERRUPT_COMP_COMPLETE |
+   VE_INTERRUPT_CAPTURE_COMPLETE, 0);
+   aspeed_video_write(video, VE_INTERRUPT_STATUS,
+  VE_INTERRUPT_COMP_COMPLETE |
+  VE_INTERRUPT_CAPTURE_COMPLETE);
+
+   if (test_bit(VIDEO_STREAMING, &video->flags) && buf)
+   aspeed_video_start_frame(video);
+   }
+
+   return IRQ_HANDLED;
+}




+static void aspeed_video_get_resolution(struct aspeed_video *video)
+{
+   bool invalid_resolution = true;
+   int rc;
+   int tries = 0;
+   u32 mds;
+   u32 src_lr_edge;
+   u32 src_tb_edge;
+   u32 sync;
+   struct v4l2_bt_timings *det = &video->detected_timings;
+
+   det->width = MIN_

[PATCH v8 1/2] dt-bindings: media: Add Aspeed Video Engine binding documentation

2018-12-11 Thread Eddie James
Document the bindings.

Signed-off-by: Eddie James 
Reviewed-by: Rob Herring 
---
 .../devicetree/bindings/media/aspeed-video.txt | 26 ++
 1 file changed, 26 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt

diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt 
b/Documentation/devicetree/bindings/media/aspeed-video.txt
new file mode 100644
index 000..78b464a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/aspeed-video.txt
@@ -0,0 +1,26 @@
+* Device tree bindings for Aspeed Video Engine
+
+The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can
+capture and compress video data from digital or analog sources.
+
+Required properties:
+ - compatible: "aspeed,ast2400-video-engine" or
+   "aspeed,ast2500-video-engine"
+ - reg:contains the offset and length of the VE memory 
region
+ - clocks: clock specifiers for the syscon clocks associated with
+   the VE (ordering must match the clock-names property)
+ - clock-names:"vclk" and "eclk"
+ - resets: reset specifier for the syscon reset associated with
+   the VE
+ - interrupts: the interrupt associated with the VE on this platform
+
+Example:
+
+video-engine@1e70 {
+compatible = "aspeed,ast2500-video-engine";
+reg = <0x1e70 0x2>;
+clocks = <&syscon ASPEED_CLK_GATE_VCLK>, <&syscon ASPEED_CLK_GATE_ECLK>;
+clock-names = "vclk", "eclk";
+resets = <&syscon ASPEED_RESET_VIDEO>;
+interrupts = <7>;
+};
-- 
1.8.3.1



[PATCH v8 0/2] media: platform: Add Aspeed Video Engine driver

2018-12-11 Thread Eddie James
From: Eddie James 

The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting as a service processor, the Video Engine can
capture the host processor graphics output.

This series adds a V4L2 driver for the VE, providing the usual V4L2 streaming
interface by way of videobuf2. Each frame, the driver triggers the hardware to
capture the host graphics output and compress it to JPEG format.

v4l2-compliance SHA: d039b47a108596ca004b11e52989054882d45888, 32 bits

Compliance test for device /dev/video0:

Driver Info:
Driver name  : aspeed-video
Card type: Aspeed Video Engine
Bus info : platform:aspeed-video
Driver version   : 4.19.6
Capabilities : 0x8521
Video Capture
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps  : 0x0521
Video Capture
Read/Write
Streaming
Extended Pix Format

Required ioctls:
test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK

Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
test VIDIOC_DV_TIMINGS_CAP: OK
test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
warn: v4l2-test-controls.cpp(853): V4L2_CID_DV_RX_POWER_PRESENT 
not found for input 0
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 3 Private Controls: 0

Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)

Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)

Test input 0:

Streaming ioctls:
test read/write: OK
test blocking wait: OK
test MMAP: OK 
test USERPTR: OK (Not Supported)
test DMABUF: Cannot test, specify --expbuf-device

Total: 48, Succeeded: 48, Failed: 0, Warnings: 1

I'm unable to run linux-next or the media tree as our system doesn't boot
without quite a few patches that aren't yet upstreamed, so I can't grab the
fix for that warning Hans mentioned.

Changes since v7:
 - Fix the check for sending the source change event to include acquiring or
   losing a signal without changing resolution
 - Fix return value of query_timings to reflect signal status
 - Added and improved a few comments

Changes since v6:
 - Refactor open/start process such that the file handle can be opened without
   a video signal present
 - Added standards and capabilities
 - Removed used of VB2_BUF_FLAG_LAST
 - Added a few comments
 - Removed a couple of functions that were only used once
 - Removed client counter

Changes since v5:
 - Rework resolution change and v4l2 timings functions again with active and
   detected timings.
 - Renamed a few things.
 - Fixed polarities in the timings.

Changes since v4:
 - Set real min and max resolution in enum_dv_timings
 - Add check for vb2_is_busy

[PATCH v8 2/2] media: platform: Add Aspeed Video Engine driver

2018-12-11 Thread Eddie James
The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting a service processor, the Video Engine can capture
the host processor graphics output.

Add a V4L2 driver to capture video data and compress it to JPEG images.
Make the video frames available through the V4L2 streaming interface.

Signed-off-by: Eddie James 
---
 MAINTAINERS   |8 +
 drivers/media/platform/Kconfig|9 +
 drivers/media/platform/Makefile   |1 +
 drivers/media/platform/aspeed-video.c | 1729 +
 4 files changed, 1747 insertions(+)
 create mode 100644 drivers/media/platform/aspeed-video.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9ceb649..1fd5fbd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2427,6 +2427,14 @@ S:   Maintained
 F: Documentation/hwmon/asc7621
 F: drivers/hwmon/asc7621.c
 
+ASPEED VIDEO ENGINE DRIVER
+M: Eddie James 
+L: linux-me...@vger.kernel.org
+L: open...@lists.ozlabs.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/media/platform/aspeed-video.c
+F: Documentation/devicetree/bindings/media/aspeed-video.txt
+
 ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
 M: Corentin Chary 
 L: acpi4asus-u...@lists.sourceforge.net
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ea33063..a505e9f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -32,6 +32,15 @@ source "drivers/media/platform/davinci/Kconfig"
 
 source "drivers/media/platform/omap/Kconfig"
 
+config VIDEO_ASPEED
+   tristate "Aspeed AST2400 and AST2500 Video Engine driver"
+   depends on VIDEO_V4L2
+   select VIDEOBUF2_DMA_CONTIG
+   help
+ Support for the Aspeed Video Engine (VE) embedded in the Aspeed
+ AST2400 and AST2500 SOCs. The VE can capture and compress video data
+ from digital or analog sources.
+
 config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index d347a55..e6deb25 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the video capture/playback device drivers.
 #
 
+obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o
 obj-$(CONFIG_VIDEO_CADENCE)+= cadence/
 obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
 obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
diff --git a/drivers/media/platform/aspeed-video.c 
b/drivers/media/platform/aspeed-video.c
new file mode 100644
index 000..dfec813
--- /dev/null
+++ b/drivers/media/platform/aspeed-video.c
@@ -0,0 +1,1729 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DEVICE_NAME"aspeed-video"
+
+#define ASPEED_VIDEO_JPEG_NUM_QUALITIES12
+#define ASPEED_VIDEO_JPEG_HEADER_SIZE  10
+#define ASPEED_VIDEO_JPEG_QUANT_SIZE   116
+#define ASPEED_VIDEO_JPEG_DCT_SIZE 34
+
+#define MAX_FRAME_RATE 60
+#define MAX_HEIGHT 1200
+#define MAX_WIDTH  1920
+#define MIN_HEIGHT 480
+#define MIN_WIDTH  640
+
+#define NUM_POLARITY_CHECKS10
+#define INVALID_RESOLUTION_RETRIES 2
+#define INVALID_RESOLUTION_DELAY   msecs_to_jiffies(250)
+#define RESOLUTION_CHANGE_DELAYmsecs_to_jiffies(500)
+#define MODE_DETECT_TIMEOUTmsecs_to_jiffies(500)
+#define STOP_TIMEOUT   msecs_to_jiffies(1000)
+#define DIRECT_FETCH_THRESHOLD 0x0c /* 1024 * 768 */
+
+#define VE_MAX_SRC_BUFFER_SIZE 0x8ca000 /* 1920 * 1200, 32bpp */
+#define VE_JPEG_HEADER_SIZE0x006000 /* 512 * 12 * 4 */
+
+#define VE_PROTECTION_KEY  0x000
+#define  VE_PROTECTION_KEY_UNLOCK  0x1a038aa8
+
+#define VE_SEQ_CTRL0x004
+#define  VE_SEQ_CTRL_TRIG_MODE_DET BIT(0)
+#define  VE_SEQ_CTRL_TRIG_CAPTURE  BIT(1)
+#define  VE_SEQ_CTRL_FORCE_IDLEBIT(2)
+#define  VE_SEQ_CTRL_MULT_FRAMEBIT(3)
+#define  VE_SEQ_CTRL_TRIG_COMP BIT(4)
+#define  VE_SEQ_CTRL_AUTO_COMP BIT(5)
+#define  VE_SEQ_CTRL_EN_WATCHDOG   BIT(7)
+#define  VE_SEQ_CTRL_YUV420BIT(10)
+#define  VE_SEQ_CTRL_COMP_FMT  GENMASK(11, 10)
+#define  VE_SEQ_CTRL_HALT  BIT(12)
+#define  VE_SEQ_CTRL_EN_WATCHDOG_COMP  BIT(14)
+#define  VE_SEQ_CTRL_TRIG_JPG  BIT(15)
+#defi

[PATCH 0/2] clk: aspeed: Setup video engine clocking

2018-12-11 Thread Eddie James
This series adds the parent clock definitions and muxing for the AST2xxx
video engine.

I sent this series before as part of my first patch set for the video engine,
but probably better to send it separately.

Eddie James (2):
  clk: aspeed: Add video engine reset index definition
  clk: aspeed: Setup video engine clocking

 drivers/clk/clk-aspeed.c | 41 ++--
 include/dt-bindings/clock/aspeed-clock.h |  1 +
 2 files changed, 40 insertions(+), 2 deletions(-)

-- 
1.8.3.1



[PATCH 1/2] clk: aspeed: Add video engine reset index definition

2018-12-11 Thread Eddie James
Add an index into the array of resets kept in the Aspeed clock driver
for the video engine. This isn't a HW bit definition.

Signed-off-by: Eddie James 
Acked-by: Stephen Boyd 
Reviewed-by: Rob Herring 
---
 include/dt-bindings/clock/aspeed-clock.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/aspeed-clock.h 
b/include/dt-bindings/clock/aspeed-clock.h
index f437386..15a9059 100644
--- a/include/dt-bindings/clock/aspeed-clock.h
+++ b/include/dt-bindings/clock/aspeed-clock.h
@@ -50,5 +50,6 @@
 #define ASPEED_RESET_I2C   7
 #define ASPEED_RESET_AHB   8
 #define ASPEED_RESET_CRT1  9
+#define ASPEED_RESET_VIDEO 10
 
 #endif
-- 
1.8.3.1



[PATCH 2/2] clk: aspeed: Setup video engine clocking

2018-12-11 Thread Eddie James
Add the video engine reset bit. Add eclk mux and clock divider table.

Signed-off-by: Eddie James 
Acked-by: Stephen Boyd 
---
 drivers/clk/clk-aspeed.c | 41 +++--
 1 file changed, 39 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 5961367..f16ce7d 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -87,7 +87,7 @@ struct aspeed_clk_gate {
 /* TODO: ask Aspeed about the actual parent data */
 static const struct aspeed_gate_data aspeed_gates[] = {
/*   clk rst   name parent  
flags */
-   [ASPEED_CLK_GATE_ECLK] ={  0, -1, "eclk-gate",  "eclk", 
0 }, /* Video Engine */
+   [ASPEED_CLK_GATE_ECLK] ={  0,  6, "eclk-gate",  "eclk", 
0 }, /* Video Engine */
[ASPEED_CLK_GATE_GCLK] ={  1,  7, "gclk-gate",  NULL,   
0 }, /* 2D engine */
[ASPEED_CLK_GATE_MCLK] ={  2, -1, "mclk-gate",  "mpll", 
CLK_IS_CRITICAL }, /* SDRAM */
[ASPEED_CLK_GATE_VCLK] ={  3,  6, "vclk-gate",  NULL,   
0 }, /* Video Capture */
@@ -113,6 +113,24 @@ struct aspeed_clk_gate {
[ASPEED_CLK_GATE_LHCCLK] =  { 28, -1, "lhclk-gate", 
"lhclk", 0 }, /* LPC master/LPC+ */
 };
 
+static const char * const eclk_parent_names[] = {
+   "mpll",
+   "hpll",
+   "dpll",
+};
+
+static const struct clk_div_table ast2500_eclk_div_table[] = {
+   { 0x0, 2 },
+   { 0x1, 2 },
+   { 0x2, 3 },
+   { 0x3, 4 },
+   { 0x4, 5 },
+   { 0x5, 6 },
+   { 0x6, 7 },
+   { 0x7, 8 },
+   { 0 }
+};
+
 static const struct clk_div_table ast2500_mac_div_table[] = {
{ 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */
{ 0x1, 4 },
@@ -192,18 +210,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char 
*name, u32 val)
 
 struct aspeed_clk_soc_data {
const struct clk_div_table *div_table;
+   const struct clk_div_table *eclk_div_table;
const struct clk_div_table *mac_div_table;
struct clk_hw *(*calc_pll)(const char *name, u32 val);
 };
 
 static const struct aspeed_clk_soc_data ast2500_data = {
.div_table = ast2500_div_table,
+   .eclk_div_table = ast2500_eclk_div_table,
.mac_div_table = ast2500_mac_div_table,
.calc_pll = aspeed_ast2500_calc_pll,
 };
 
 static const struct aspeed_clk_soc_data ast2400_data = {
.div_table = ast2400_div_table,
+   .eclk_div_table = ast2400_div_table,
.mac_div_table = ast2400_div_table,
.calc_pll = aspeed_ast2400_calc_pll,
 };
@@ -317,6 +338,7 @@ struct aspeed_reset {
[ASPEED_RESET_PECI] = 10,
[ASPEED_RESET_I2C]  =  2,
[ASPEED_RESET_AHB]  =  1,
+   [ASPEED_RESET_VIDEO]=  6,
 
/*
 * SCUD4 resets start at an offset to separate them from
@@ -522,6 +544,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
return PTR_ERR(hw);
aspeed_clk_data->hws[ASPEED_CLK_24M] = hw;
 
+   hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names,
+ARRAY_SIZE(eclk_parent_names), 0,
+scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0,
+&aspeed_clk_lock);
+   if (IS_ERR(hw))
+   return PTR_ERR(hw);
+   aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw;
+
+   hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0,
+  scu_base + ASPEED_CLK_SELECTION, 28,
+  3, 0, soc_data->eclk_div_table,
+  &aspeed_clk_lock);
+   if (IS_ERR(hw))
+   return PTR_ERR(hw);
+   aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw;
+
/*
 * TODO: There are a number of clocks that not included in this driver
 * as more information is required:
@@ -531,7 +569,6 @@ static int aspeed_clk_probe(struct platform_device *pdev)
 *   RGMII
 *   RMII
 *   UART[1..5] clock source mux
-*   Video Engine (ECLK) mux and clock divider
 */
 
for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
-- 
1.8.3.1



Re: [PATCH] hwmon (occ): Fix potential integer overflow

2019-01-07 Thread Eddie James




On 01/07/2019 12:34 PM, Gustavo A. R. Silva wrote:

Cast get_unaligned_be32(...) to u64 in order to give the compiler
complete information about the proper arithmetic to use and avoid
a potential integer overflow.

Notice that such function call is used in contexts that expect
expressions of type u64 (64 bits, unsigned); and the following
expressions are currently being evaluated using 32-bit
arithmetic:

val = get_unaligned_be32(&power->update_tag) *
 occ->powr_sample_time_us;

val = get_unaligned_be32(&power->vdn.update_tag) *
 occ->powr_sample_time_us;


Thanks,

Reviewed-by: Eddie James 



Addresses-Coverity-ID: 1442357 ("Unintentional integer overflow")
Addresses-Coverity-ID: 1442476 ("Unintentional integer overflow")
Addresses-Coverity-ID: 1442508 ("Unintentional integer overflow")
Fixes: ff692d80b2e2 ("hwmon (occ): Add sensor types and versions")
Cc: sta...@vger.kernel.org
Signed-off-by: Gustavo A. R. Silva 
---
  drivers/hwmon/occ/common.c | 24 
  1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 423903f87955..391118c8aae8 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -380,8 +380,8 @@ static ssize_t occ_show_power_1(struct device *dev,
val *= 100ULL;
break;
case 2:
-   val = get_unaligned_be32(&power->update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->update_tag) *
+  occ->powr_sample_time_us;
break;
case 3:
val = get_unaligned_be16(&power->value) * 100ULL;
@@ -425,8 +425,8 @@ static ssize_t occ_show_power_2(struct device *dev,
   &power->update_tag);
break;
case 2:
-   val = get_unaligned_be32(&power->update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->update_tag) *
+  occ->powr_sample_time_us;
break;
case 3:
val = get_unaligned_be16(&power->value) * 100ULL;
@@ -463,8 +463,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->system.update_tag);
break;
case 2:
-   val = get_unaligned_be32(&power->system.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->system.update_tag) *
+  occ->powr_sample_time_us;
break;
case 3:
val = get_unaligned_be16(&power->system.value) * 100ULL;
@@ -477,8 +477,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->proc.update_tag);
break;
case 6:
-   val = get_unaligned_be32(&power->proc.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->proc.update_tag) *
+  occ->powr_sample_time_us;
break;
case 7:
val = get_unaligned_be16(&power->proc.value) * 100ULL;
@@ -491,8 +491,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->vdd.update_tag);
break;
case 10:
-   val = get_unaligned_be32(&power->vdd.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->vdd.update_tag) *
+  occ->powr_sample_time_us;
break;
case 11:
val = get_unaligned_be16(&power->vdd.value) * 100ULL;
@@ -505,8 +505,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->vdn.update_tag);
break;
case 14:
-   val = get_unaligned_be32(&power->vdn.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->vdn.update_tag) *
+  occ->powr_sample_time_us;
break;
case 15:
val = get_unaligned_be16(&power->vdn.value) * 100ULL;




[PATCH v6 01/10] dt-bindings: fsi: Add P9 OCC device documentation

2018-11-08 Thread Eddie James
From: Eddie James 

Document the bindings for the FSI-attached POWER9 On-Chip Controller.

Signed-off-by: Eddie James 
---
 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt | 16 
 1 file changed, 16 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt

diff --git a/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt 
b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
new file mode 100644
index 000..99ca986
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
@@ -0,0 +1,16 @@
+Device-tree bindings for FSI-attached POWER9 On-Chip Controller (OCC)
+-
+
+This is the binding for the P9 On-Chip Controller accessed over FSI from a
+service processor. See fsi.txt for details on bindings for FSI slave and CFAM
+nodes. The OCC is not an FSI slave device itself, rather it is accessed
+through the SBE fifo.
+
+Required properties:
+ - compatible = "ibm,p9-occ"
+
+Examples:
+
+occ {
+compatible = "ibm,p9-occ";
+};
-- 
1.8.3.1



[PATCH v12 0/8] i2c: Add FSI-attached I2C master algorithm

2018-07-17 Thread Eddie James
This series adds an algorithm for an I2C master physically located on an FSI
slave device. The I2C master has multiple ports, each of which may be connected
to an I2C slave. Access to the I2C master registers is achieved over FSI bus.

Due to the multi-port nature of the I2C master, the driver instantiates a new
I2C adapter for each port connected to a slave. The connected ports should be
defined in the device tree under the I2C master device.

Changes since v11
 - Remove I2C_FUNC_10BIT_ADDR since we don't support it
 - Don't shift the msg->addr when encoding the command; use it as-is

Changes since v10
 - Add myself to maintainers
 - Rework the reset path. Use generic bus recovery and only do the reset if SDA
   is low.
 - Fixed email signature on dts patch
 - Pulled in Ben H's fix for use-after-free in remove()

Changes since v9
 - Switch the status masks to use a combination of bits rather than directly
   coding the value
 - Remove the interrupt mask definition as it was unused
 - Fixed return value of master_xfer function (return number of xfrs, not 0)

Changes since v8
 - Drop unecessary else statements
 - Use i++ instead of ++i
 - Use kzalloc/kfree instead of devm_kzalloc/devm_kfree for port structure
 - Drop the list_empty check in remove

Changes since v7
 - Fix grammer in Kconfig (a -> an)
 - Change I2C registers to use BIT and GENMASK
 - Remove custom macros and use FIELD_PREP and FIELD_GET
 - Fix a few unecessary initializations and "return rc" that are always zero
 - Clean up the read/write fifo functions a bit
 - Few other clean-up items

Changes since v6
 - Remove spinlock for reset functionality; it's unecessary and doesn't work
   with the latest FSI core.
 - Use a mutex instead of a semaphore, and don't wait for timeout to get the
   lock.
 - Use usleeps instead of schedule_timeout; it's not worth the overhead when
   the wait should be very short in between sending the command and receiving
   the response.

Changes since v5
 - Fix reset functionality and do a reset after every transfer failure

eaja...@linux.vnet.ibm.com (8):
  dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation
  i2c: Add FSI-attached I2C master algorithm
  i2c: fsi: Add port structures
  i2c: fsi: Add abort and hardware reset procedures
  i2c: fsi: Add transfer implementation
  i2c: fsi: Add I2C master locking
  i2c: fsi: Add bus recovery
  MAINTAINERS: Add Eddie as the maintainer for the FSI-attached I2C
driver

 Documentation/devicetree/bindings/i2c/i2c-fsi.txt |  40 ++
 MAINTAINERS   |   8 +
 drivers/i2c/busses/Kconfig|  11 +
 drivers/i2c/busses/Makefile   |   1 +
 drivers/i2c/busses/i2c-fsi.c  | 752 ++
 5 files changed, 812 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

-- 
1.8.3.1



[PATCH v12 3/8] i2c: fsi: Add port structures

2018-07-17 Thread Eddie James
From: "eaja...@linux.vnet.ibm.com" 

Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports for each master are defined in the devicetree.

Signed-off-by: Eddie James 
---
 drivers/i2c/busses/i2c-fsi.c | 91 
 1 file changed, 91 insertions(+)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 3725db1..1d9e9bf 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -17,7 +17,10 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #define FSI_ENGID_I2C  0x7
 
@@ -128,6 +131,14 @@
 struct fsi_i2c_master {
struct fsi_device   *fsi;
u8  fifo_size;
+   struct list_headports;
+};
+
+struct fsi_i2c_port {
+   struct list_headlist;
+   struct i2c_adapter  adapter;
+   struct fsi_i2c_master   *master;
+   u16 port;
 };
 
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -181,9 +192,38 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
 }
 
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+   int rc;
+   struct fsi_device *fsi = port->master->fsi;
+   u32 mode, dummy = 0;
+
+   rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   if (FIELD_GET(I2C_MODE_PORT, mode) == port->port)
+   return 0;
+
+   mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
+   rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+   if (rc)
+   return rc;
+
+   /* reset engine when port is changed */
+   return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
 {
+   int rc;
+   struct fsi_i2c_port *port = adap->algo_data;
+
+   rc = fsi_i2c_set_port(port);
+   if (rc)
+   return rc;
+
return -EOPNOTSUPP;
 }
 
@@ -201,23 +241,73 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
 static int fsi_i2c_probe(struct device *dev)
 {
struct fsi_i2c_master *i2c;
+   struct fsi_i2c_port *port;
+   struct device_node *np;
int rc;
+   u32 port_no;
 
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
 
i2c->fsi = to_fsi_dev(dev);
+   INIT_LIST_HEAD(&i2c->ports);
 
rc = fsi_i2c_dev_init(i2c);
if (rc)
return rc;
 
+   /* Add adapter for each i2c port of the master. */
+   for_each_available_child_of_node(dev->of_node, np) {
+   rc = of_property_read_u32(np, "reg", &port_no);
+   if (rc || port_no > USHRT_MAX)
+   continue;
+
+   port = kzalloc(sizeof(*port), GFP_KERNEL);
+   if (!port)
+   break;
+
+   port->master = i2c;
+   port->port = port_no;
+
+   port->adapter.owner = THIS_MODULE;
+   port->adapter.dev.of_node = np;
+   port->adapter.dev.parent = dev;
+   port->adapter.algo = &fsi_i2c_algorithm;
+   port->adapter.algo_data = port;
+
+   snprintf(port->adapter.name, sizeof(port->adapter.name),
+"i2c_bus-%u", port_no);
+
+   rc = i2c_add_adapter(&port->adapter);
+   if (rc < 0) {
+   dev_err(dev, "Failed to register adapter: %d\n", rc);
+   kfree(port);
+   continue;
+   }
+
+   list_add(&port->list, &i2c->ports);
+   }
+
dev_set_drvdata(dev, i2c);
 
return 0;
 }
 
+static int fsi_i2c_remove(struct device *dev)
+{
+   struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+   struct fsi_i2c_port *port, *tmp;
+
+   list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
+   list_del(&port->list);
+   i2c_del_adapter(&port->adapter);
+   kfree(port);
+   }
+
+   return 0;
+}
+
 static const struct fsi_device_id fsi_i2c_ids[] = {
{ FSI_ENGID_I2C, FSI_VERSION_ANY },
{ }
@@ -229,6 +319,7 @@ static int fsi_i2c_probe(struct device *dev)
.name = "i2c-fsi",
.bus = &fsi_bus_type,
.probe = fsi_i2c_probe,
+   .remove = fsi_i2c_remove,
},
 };
 
-- 
1.8.3.1



  1   2   3   4   5   6   >