The patch exposes the available i2c busses on the PowerNV platform
to the kernel and implements the bus driver to support i2c and
smbus commands.
The driver uses the platform device infrastructure to probe the busses
on the platform and registers them with the i2c driver framework.
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/opal.h | 21 ++
arch/powerpc/platforms/powernv/opal-wrappers.S | 1
arch/powerpc/platforms/powernv/opal.c | 48 +++-
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1
drivers/i2c/busses/i2c-opal.c | 276 ++++++++++++++++++++++++
6 files changed, 349 insertions(+), 9 deletions(-)
create mode 100644 drivers/i2c/busses/i2c-opal.c
@@ -644,17 +644,10 @@ static void __init opal_dump_region_init(void)pr_warn("DUMP: Failed to register kernel log buffer. ""rc = %d\n",rc);}-staticint__initopal_init(void)++staticvoidopal_console_create_devs(void){structdevice_node*np,*consoles;-const__be32*irqs;-intrc,i,irqlen;--opal_node=of_find_node_by_path("/ibm,opal");-if(!opal_node){-pr_warn("opal: Node not found\n");-return-ENODEV;-}/* Register OPAL consoles if any ports */if(firmware_has_feature(FW_FEATURE_OPALv2))
@@ -670,6 +663,21 @@ static int __init opal_init(void)of_node_put(consoles);}+}++staticvoidopal_i2c_create_devs(void)+{+structdevice_node*np;++for_each_compatible_node(np,NULL,"ibm,power8-i2c-port")+of_platform_device_create(np,NULL,NULL);+}++staticvoidopal_request_interrupts(void)+{+const__be32*irqs;+intrc,i,irqlen;+/* Find all OPAL interrupts and request them */irqs=of_get_property(opal_node,"opal-interrupts",&irqlen);pr_debug("opal: Found %d interrupts reserved for OPAL\n",
@@ -0,0 +1,276 @@+/*+*IBMOPALI2Cdriver+*Copyright(C)2014IBM+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram.+*/++#include<linux/module.h>+#include<linux/kernel.h>+#include<linux/slab.h>+#include<linux/i2c.h>+#include<linux/device.h>+#include<linux/platform_device.h>+#include<linux/of.h>+#include<linux/mm.h>+#include<asm/opal.h>+#include<asm/firmware.h>++staticinti2c_opal_send_request(u32bus_id,structopal_i2c_request*req)+{+structopal_msgmsg;+inttoken,rc;++token=opal_async_get_token_interruptible();+if(token<0){+if(token!=-ERESTARTSYS)+pr_err("Failed to get the async token\n");++returntoken;+}++rc=opal_i2c_request(token,bus_id,req);+if(rc!=OPAL_ASYNC_COMPLETION){+rc=-EIO;+gotoexit;+}++rc=opal_async_wait_response(token,&msg);+if(rc){+rc=-EIO;+gotoexit;+}++rc=be64_to_cpu(msg.params[1]);+if(rc!=OPAL_SUCCESS){+rc=-EIO;+gotoexit;+}++exit:+opal_async_release_token(token);+returnrc;+}++staticinti2c_opal_master_xfer(structi2c_adapter*adap,structi2c_msg*msgs,+intnum)+{+unsignedlongopal_id=(unsignedlong)adap->algo_data;+structopal_i2c_requestreq;+intrc,i;++/* We only support fairly simple combinations here of one+*ortwomessages+*/+memset(&req,0,sizeof(req));+switch(num){+case0:+return0;+case1:+req.type=(msgs[0].flags&I2C_M_RD)?+OPAL_I2C_RAW_READ:OPAL_I2C_RAW_WRITE;+req.addr=cpu_to_be16(msgs[0].addr);+req.size=cpu_to_be32(msgs[0].len);+req.buffer_ra=cpu_to_be64(__pa(msgs[0].buf));+break;+case2:+/* For two messages, we basically support only simple+*smbustransactionsofawriteplusaread.Wemight+*wanttoallowalsotwowritesbutwe'dhavetobounce+*thedataintoasinglebuffer.+*/+if((msgs[0].flags&I2C_M_RD)||!(msgs[1].flags&I2C_M_RD))+return-EIO;+if(msgs[0].len>4)+return-EIO;+if(msgs[0].addr!=msgs[1].addr)+return-EIO;+req.type=OPAL_I2C_SM_READ;+req.addr=cpu_to_be16(msgs[0].addr);+req.subaddr_sz=msgs[0].len;+for(i=0;i<msgs[0].len;i++)+req.subaddr=(req.subaddr<<8)|msgs[0].buf[i];+req.subaddr=cpu_to_be32(req.subaddr);+req.size=cpu_to_be32(msgs[1].len);+req.buffer_ra=cpu_to_be64(__pa(msgs[1].buf));+break;+default:+return-EIO;+}++rc=i2c_opal_send_request(opal_id,&req);+if(rc)+returnrc;++returnnum;+}++staticinti2c_opal_smbus_xfer(structi2c_adapter*adap,u16addr,+unsignedshortflags,charread_write,+u8command,intsize,unioni2c_smbus_data*data)+{+unsignedlongopal_id=(unsignedlong)adap->algo_data;+structopal_i2c_requestreq;+u8local[2];+intrc;++memset(&req,0,sizeof(req));++req.addr=cpu_to_be16(addr);+switch(size){+caseI2C_SMBUS_BYTE:+req.buffer_ra=cpu_to_be64(__pa(&data->byte));+req.size=cpu_to_be32(1);+/* Fall through */+caseI2C_SMBUS_QUICK:+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_RAW_READ:OPAL_I2C_RAW_WRITE;+break;+caseI2C_SMBUS_BYTE_DATA:+req.buffer_ra=cpu_to_be64(__pa(&data->byte));+req.size=cpu_to_be32(1);+req.subaddr=cpu_to_be32(command);+req.subaddr_sz=1;+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_SM_READ:OPAL_I2C_SM_WRITE;+break;+caseI2C_SMBUS_WORD_DATA:+if(!read_write){+local[0]=data->word&0xff;+local[1]=(data->word>>8)&0xff;+}+req.buffer_ra=cpu_to_be64(__pa(local));+req.size=cpu_to_be32(2);+req.subaddr=cpu_to_be32(command);+req.subaddr_sz=1;+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_SM_READ:OPAL_I2C_SM_WRITE;+break;+caseI2C_SMBUS_I2C_BLOCK_DATA:+req.buffer_ra=cpu_to_be64(__pa(&data->block[1]));+req.size=cpu_to_be32(data->block[0]);+req.subaddr=cpu_to_be32(command);+req.subaddr_sz=1;+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_SM_READ:OPAL_I2C_SM_WRITE;+break;+default:+return-EINVAL;+}++rc=i2c_opal_send_request(opal_id,&req);+if(!rc&&read_write&&size==I2C_SMBUS_WORD_DATA){+data->word=((u16)local[1])<<8;+data->word|=local[0];+}++returnrc;+}++staticu32i2c_opal_func(structi2c_adapter*adapter)+{+returnI2C_FUNC_I2C|I2C_FUNC_SMBUS_QUICK|I2C_FUNC_SMBUS_BYTE|+I2C_FUNC_SMBUS_BYTE_DATA|I2C_FUNC_SMBUS_WORD_DATA|+I2C_FUNC_SMBUS_I2C_BLOCK;+}++staticconststructi2c_algorithmi2c_opal_algo={+.master_xfer=i2c_opal_master_xfer,+.smbus_xfer=i2c_opal_smbus_xfer,+.functionality=i2c_opal_func,+};++staticinti2c_opal_probe(structplatform_device*pdev)+{+structi2c_adapter*adapter;+constchar*pname;+u32opal_id;+intrc;++if(!pdev->dev.of_node)+return-ENODEV;+rc=of_property_read_u32(pdev->dev.of_node,"ibm,opal-id",&opal_id);+if(rc){+dev_err(&pdev->dev,"Missing ibm,opal-id property !\n");+return-EIO;+}+adapter=kzalloc(sizeof(structi2c_adapter),GFP_KERNEL);+if(!adapter)+return-ENOMEM;+adapter->algo=&i2c_opal_algo;+adapter->algo_data=(void*)(unsignedlong)opal_id;+adapter->dev.parent=&pdev->dev;+adapter->dev.of_node=of_node_get(pdev->dev.of_node);+pname=of_get_property(pdev->dev.of_node,"port-name",NULL);+if(pname)+strlcpy(adapter->name,pname,sizeof(adapter->name));+else+strlcpy(adapter->name,"opal",sizeof(adapter->name));++platform_set_drvdata(pdev,adapter);+rc=i2c_add_adapter(adapter);+if(rc)+dev_err(&pdev->dev,"Failed to register the i2c adapter\n");++returnrc;+}++staticinti2c_opal_remove(structplatform_device*pdev)+{+structi2c_adapter*adapter=platform_get_drvdata(pdev);++i2c_del_adapter(adapter);++kfree(adapter);++return0;+}++staticconststructof_device_idi2c_opal_of_match[]={+{+.compatible="ibm,power8-i2c-port",+},+{}+};+MODULE_DEVICE_TABLE(of,i2c_opal_of_match);++staticstructplatform_driveri2c_opal_driver={+.probe=i2c_opal_probe,+.remove=i2c_opal_remove,+.driver={+.name="i2c-opal",+.owner=THIS_MODULE,+.of_match_table=i2c_opal_of_match,+},+};++staticint__initi2c_opal_init(void)+{+if(!firmware_has_feature(FW_FEATURE_OPAL))+return-ENODEV;++returnplatform_driver_register(&i2c_opal_driver);+}++staticvoid__exiti2c_opal_exit(void)+{+returnplatform_driver_unregister(&i2c_opal_driver);+}++MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");+MODULE_DESCRIPTION("IBM OPAL I2C driver");+MODULE_LICENSE("GPL");++module_init(i2c_opal_init);+module_exit(i2c_opal_exit);
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2014-11-12 06:34:30
On Mon, 2014-11-10 at 11:35 +0530, Neelesh Gupta wrote:
The patch exposes the available i2c busses on the PowerNV platform
to the kernel and implements the bus driver to support i2c and
smbus commands.
The driver uses the platform device infrastructure to probe the busses
on the platform and registers them with the i2c driver framework.
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This version slightly modified removes the unrelated gunk in opal.c
(but needs to apply on top of some other patches in the powerpc tree)
The driver is the same, it's only the
arch/powerpc/platform/powernv/opal.c init bits that get cleaned up and
simplified.
From: Neelesh Gupta <redacted>
Date: Fri, 7 Nov 2014 16:20:07 +1100
Subject: [PATCH v2] i2c: Driver to expose PowerNV platform i2c busses
The patch exposes the available i2c busses on the PowerNV platform
to the kernel and implements the bus driver to support i2c and
smbus commands.
If the devices are found on the device tree for a given bus/adapter,
the platform init code registers them to the core and binds them
a static bus/adapter number, otherwise the driver registers the
adapter to get the adapter number dynamically.
Results:
--------
[root@tul176p1 ~]# ls -l /sys/class/i2c-adapter/
total 0
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-1 -> ../../devices/i2c-maxim:0/i2c-1
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-2 -> ../../devices/i2c-maxim:1/i2c-2
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-3 -> ../../devices/i2c-vpd:0/i2c-3
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-4 -> ../../devices/i2c-vpd:1/i2c-4
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-5 -> ../../devices/i2c-vpd:2/i2c-5
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-6 -> ../../devices/i2c-vpd:3/i2c-6
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-7 -> ../../devices/i2c-vpd:4/i2c-7
lrwxrwxrwx 1 root root 0 Oct 27 12:41 i2c-8 -> ../../devices/i2c-vpd:5/i2c-8
[root@tul176p1 ~]# ls -l /sys/class/i2c-adapter/i2c-1/
total 0
drwxr-xr-x 2 root root 0 Oct 27 12:41 1-0032
drwxr-xr-x 2 root root 0 Oct 27 12:41 1-0035
drwxr-xr-x 2 root root 0 Oct 27 12:41 1-0036
drwxr-xr-x 2 root root 0 Oct 27 12:41 1-0039
--w------- 1 root root 65536 Oct 27 12:44 delete_device
lrwxrwxrwx 1 root root 0 Oct 27 12:44 device -> ../../i2c-maxim:0
-r--r--r-- 1 root root 65536 Oct 27 12:41 name
--w------- 1 root root 65536 Oct 27 12:44 new_device
lrwxrwxrwx 1 root root 0 Oct 27 12:41 subsystem -> ../../../bus/i2c
-rw-r--r-- 1 root root 65536 Oct 27 12:41 uevent
[root@tul176p1 ~]# cat /sys/class/i2c-adapter/i2c-1/1-0032/name
slot-C10-C11
[root@tul176p1 ~]#
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/opal.h | 22 +-
arch/powerpc/platforms/powernv/opal-wrappers.S | 1 +
arch/powerpc/platforms/powernv/opal.c | 23 ++-
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-opal.c | 276 +++++++++++++++++++++++++
6 files changed, 328 insertions(+), 6 deletions(-)
create mode 100644 drivers/i2c/busses/i2c-opal.c
@@ -777,11 +788,6 @@ void opal_shutdown(void)opal_unregister_dump_region(OPAL_DUMP_REGION_LOG_BUF);}-/* Export this so that test modules can use it */-EXPORT_SYMBOL_GPL(opal_invalid_call);-EXPORT_SYMBOL_GPL(opal_ipmi_send);-EXPORT_SYMBOL_GPL(opal_ipmi_recv);-/* Convert a region of vmalloc memory to an opal sg list */structopal_sg_list*opal_vmalloc_to_sg_list(void*vmalloc_addr,unsignedlongvmalloc_size)
@@ -844,3 +850,10 @@ void opal_free_sg_list(struct opal_sg_list *sg)sg=NULL;}}++/* Export this so that test modules can use it */+EXPORT_SYMBOL_GPL(opal_invalid_call);+EXPORT_SYMBOL_GPL(opal_ipmi_send);+EXPORT_SYMBOL_GPL(opal_ipmi_recv);+EXPORT_SYMBOL_GPL(opal_i2c_request);+
@@ -0,0 +1,276 @@+/*+*IBMOPALI2Cdriver+*Copyright(C)2014IBM+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram.+*/++#include<linux/module.h>+#include<linux/kernel.h>+#include<linux/slab.h>+#include<linux/i2c.h>+#include<linux/device.h>+#include<linux/platform_device.h>+#include<linux/of.h>+#include<linux/mm.h>+#include<asm/opal.h>+#include<asm/firmware.h>++staticinti2c_opal_send_request(u32bus_id,structopal_i2c_request*req)+{+structopal_msgmsg;+inttoken,rc;++token=opal_async_get_token_interruptible();+if(token<0){+if(token!=-ERESTARTSYS)+pr_err("Failed to get the async token\n");++returntoken;+}++rc=opal_i2c_request(token,bus_id,req);+if(rc!=OPAL_ASYNC_COMPLETION){+rc=-EIO;+gotoexit;+}++rc=opal_async_wait_response(token,&msg);+if(rc){+rc=-EIO;+gotoexit;+}++rc=be64_to_cpu(msg.params[1]);+if(rc!=OPAL_SUCCESS){+rc=-EIO;+gotoexit;+}++exit:+opal_async_release_token(token);+returnrc;+}++staticinti2c_opal_master_xfer(structi2c_adapter*adap,structi2c_msg*msgs,+intnum)+{+unsignedlongopal_id=(unsignedlong)adap->algo_data;+structopal_i2c_requestreq;+intrc,i;++/* We only support fairly simple combinations here of one+*ortwomessages+*/+memset(&req,0,sizeof(req));+switch(num){+case0:+return0;+case1:+req.type=(msgs[0].flags&I2C_M_RD)?+OPAL_I2C_RAW_READ:OPAL_I2C_RAW_WRITE;+req.addr=cpu_to_be16(msgs[0].addr);+req.size=cpu_to_be32(msgs[0].len);+req.buffer_ra=cpu_to_be64(__pa(msgs[0].buf));+break;+case2:+/* For two messages, we basically support only simple+*smbustransactionsofawriteplusaread.Wemight+*wanttoallowalsotwowritesbutwe'dhavetobounce+*thedataintoasinglebuffer.+*/+if((msgs[0].flags&I2C_M_RD)||!(msgs[1].flags&I2C_M_RD))+return-EIO;+if(msgs[0].len>4)+return-EIO;+if(msgs[0].addr!=msgs[1].addr)+return-EIO;+req.type=OPAL_I2C_SM_READ;+req.addr=cpu_to_be16(msgs[0].addr);+req.subaddr_sz=msgs[0].len;+for(i=0;i<msgs[0].len;i++)+req.subaddr=(req.subaddr<<8)|msgs[0].buf[i];+req.subaddr=cpu_to_be32(req.subaddr);+req.size=cpu_to_be32(msgs[1].len);+req.buffer_ra=cpu_to_be64(__pa(msgs[1].buf));+break;+default:+return-EIO;+}++rc=i2c_opal_send_request(opal_id,&req);+if(rc)+returnrc;++returnnum;+}++staticinti2c_opal_smbus_xfer(structi2c_adapter*adap,u16addr,+unsignedshortflags,charread_write,+u8command,intsize,unioni2c_smbus_data*data)+{+unsignedlongopal_id=(unsignedlong)adap->algo_data;+structopal_i2c_requestreq;+u8local[2];+intrc;++memset(&req,0,sizeof(req));++req.addr=cpu_to_be16(addr);+switch(size){+caseI2C_SMBUS_BYTE:+req.buffer_ra=cpu_to_be64(__pa(&data->byte));+req.size=cpu_to_be32(1);+/* Fall through */+caseI2C_SMBUS_QUICK:+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_RAW_READ:OPAL_I2C_RAW_WRITE;+break;+caseI2C_SMBUS_BYTE_DATA:+req.buffer_ra=cpu_to_be64(__pa(&data->byte));+req.size=cpu_to_be32(1);+req.subaddr=cpu_to_be32(command);+req.subaddr_sz=1;+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_SM_READ:OPAL_I2C_SM_WRITE;+break;+caseI2C_SMBUS_WORD_DATA:+if(!read_write){+local[0]=data->word&0xff;+local[1]=(data->word>>8)&0xff;+}+req.buffer_ra=cpu_to_be64(__pa(local));+req.size=cpu_to_be32(2);+req.subaddr=cpu_to_be32(command);+req.subaddr_sz=1;+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_SM_READ:OPAL_I2C_SM_WRITE;+break;+caseI2C_SMBUS_I2C_BLOCK_DATA:+req.buffer_ra=cpu_to_be64(__pa(&data->block[1]));+req.size=cpu_to_be32(data->block[0]);+req.subaddr=cpu_to_be32(command);+req.subaddr_sz=1;+req.type=(read_write==I2C_SMBUS_READ)?+OPAL_I2C_SM_READ:OPAL_I2C_SM_WRITE;+break;+default:+return-EINVAL;+}++rc=i2c_opal_send_request(opal_id,&req);+if(!rc&&read_write&&size==I2C_SMBUS_WORD_DATA){+data->word=((u16)local[1])<<8;+data->word|=local[0];+}++returnrc;+}++staticu32i2c_opal_func(structi2c_adapter*adapter)+{+returnI2C_FUNC_I2C|I2C_FUNC_SMBUS_QUICK|I2C_FUNC_SMBUS_BYTE|+I2C_FUNC_SMBUS_BYTE_DATA|I2C_FUNC_SMBUS_WORD_DATA|+I2C_FUNC_SMBUS_I2C_BLOCK;+}++staticconststructi2c_algorithmi2c_opal_algo={+.master_xfer=i2c_opal_master_xfer,+.smbus_xfer=i2c_opal_smbus_xfer,+.functionality=i2c_opal_func,+};++staticinti2c_opal_probe(structplatform_device*pdev)+{+structi2c_adapter*adapter;+constchar*pname;+u32opal_id;+intrc;++if(!pdev->dev.of_node)+return-ENODEV;+rc=of_property_read_u32(pdev->dev.of_node,"ibm,opal-id",&opal_id);+if(rc){+dev_err(&pdev->dev,"Missing ibm,opal-id property !\n");+return-EIO;+}+adapter=kzalloc(sizeof(structi2c_adapter),GFP_KERNEL);+if(!adapter)+return-ENOMEM;+adapter->algo=&i2c_opal_algo;+adapter->algo_data=(void*)(unsignedlong)opal_id;+adapter->dev.parent=&pdev->dev;+adapter->dev.of_node=of_node_get(pdev->dev.of_node);+pname=of_get_property(pdev->dev.of_node,"port-name",NULL);+if(pname)+strlcpy(adapter->name,pname,sizeof(adapter->name));+else+strlcpy(adapter->name,"opal",sizeof(adapter->name));++platform_set_drvdata(pdev,adapter);+rc=i2c_add_adapter(adapter);+if(rc)+dev_err(&pdev->dev,"Failed to register the i2c adapter\n");++returnrc;+}++staticinti2c_opal_remove(structplatform_device*pdev)+{+structi2c_adapter*adapter=platform_get_drvdata(pdev);++i2c_del_adapter(adapter);++kfree(adapter);++return0;+}++staticconststructof_device_idi2c_opal_of_match[]={+{+.compatible="ibm,power8-i2c-port",+},+{}+};+MODULE_DEVICE_TABLE(of,i2c_opal_of_match);++staticstructplatform_driveri2c_opal_driver={+.probe=i2c_opal_probe,+.remove=i2c_opal_remove,+.driver={+.name="i2c-opal",+.owner=THIS_MODULE,+.of_match_table=i2c_opal_of_match,+},+};++staticint__initi2c_opal_init(void)+{+if(!firmware_has_feature(FW_FEATURE_OPAL))+return-ENODEV;++returnplatform_driver_register(&i2c_opal_driver);+}++staticvoid__exiti2c_opal_exit(void)+{+returnplatform_driver_unregister(&i2c_opal_driver);+}++MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");+MODULE_DESCRIPTION("IBM OPAL I2C driver");+MODULE_LICENSE("GPL");++module_init(i2c_opal_init);+module_exit(i2c_opal_exit);
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2014-11-13 05:36:29
On Wed, 2014-11-12 at 17:07 +1100, Benjamin Herrenschmidt wrote:
On Mon, 2014-11-10 at 11:35 +0530, Neelesh Gupta wrote:
quoted
The patch exposes the available i2c busses on the PowerNV platform
to the kernel and implements the bus driver to support i2c and
smbus commands.
The driver uses the platform device infrastructure to probe the busses
on the platform and registers them with the i2c driver framework.
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This version slightly modified removes the unrelated gunk in opal.c
(but needs to apply on top of some other patches in the powerpc tree)
The driver is the same, it's only the
arch/powerpc/platform/powernv/opal.c init bits that get cleaned up and
simplified.
From: Neelesh Gupta <redacted>
Date: Fri, 7 Nov 2014 16:20:07 +1100
Subject: [PATCH v2] i2c: Driver to expose PowerNV platform i2c busses
The patch exposes the available i2c busses on the PowerNV platform
to the kernel and implements the bus driver to support i2c and
smbus commands.
If the devices are found on the device tree for a given bus/adapter,
the platform init code registers them to the core and binds them
a static bus/adapter number, otherwise the driver registers the
adapter to get the adapter number dynamically.
So the driver part depends on the arch/powerpc changes.
I added Wolfram the i2c maintainer to CC. Wolfram are you happy if we take this
through the powerpc tree with your ack, or I can do a topic branch?
cheers
From: Wolfram Sang <hidden> Date: 2014-11-13 07:57:06
Hi,
I am basically fine if this goes via the powerpc-tree and I was hoping
that I could ack it right now. However, the driver looks a bit rushed
and definately needs updates before it is ready to go.
On Mon, Nov 10, 2014 at 11:35:39AM +0530, Neelesh Gupta wrote:
The patch exposes the available i2c busses on the PowerNV platform
to the kernel and implements the bus driver to support i2c and
smbus commands.
The driver uses the platform device infrastructure to probe the busses
on the platform and registers them with the i2c driver framework.
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
++static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req)+{+ struct opal_msg msg;+ int token, rc;++ token = opal_async_get_token_interruptible();+ if (token < 0) {+ if (token != -ERESTARTSYS)+ pr_err("Failed to get the async token\n");++ return token;+ }++ rc = opal_i2c_request(token, bus_id, req);+ if (rc != OPAL_ASYNC_COMPLETION) {+ rc = -EIO;+ goto exit;+ }++ rc = opal_async_wait_response(token, &msg);+ if (rc) {+ rc = -EIO;+ goto exit;
Is it really -EIO? Maybe -ETIMEDOUT?
quoted hunk
+ }++ rc = be64_to_cpu(msg.params[1]);+ if (rc != OPAL_SUCCESS) {+ rc = -EIO;+ goto exit;+ }++exit:+ opal_async_release_token(token);+ return rc;+}++static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,+ int num)+{+ unsigned long opal_id = (unsigned long)adap->algo_data;+ struct opal_i2c_request req;+ int rc, i;++ /* We only support fairly simple combinations here of one+ * or two messages+ */
I don't think you should offer I2C_FUNC_I2C with those limitations. Is
there a case you really needs this?
quoted hunk
+ memset(&req, 0, sizeof(req));+ switch (num) {+ case 0:+ return 0;+ case 1:+ req.type = (msgs[0].flags & I2C_M_RD) ?+ OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;+ req.addr = cpu_to_be16(msgs[0].addr);+ req.size = cpu_to_be32(msgs[0].len);+ req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf));+ break;+ case 2:+ /* For two messages, we basically support only simple+ * smbus transactions of a write plus a read. We might+ * want to allow also two writes but we'd have to bounce+ * the data into a single buffer.+ */+ if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD))+ return -EIO;+ if (msgs[0].len > 4)+ return -EIO;+ if (msgs[0].addr != msgs[1].addr)+ return -EIO;
-EOPNOTSUPP? Please check Documentation/i2c/fault-codes for the error
codes we use.
Please put thos right below the functions it references.
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
+static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req)+{+ struct opal_msg msg;+ int token, rc;++ token = opal_async_get_token_interruptible();+ if (token < 0) {+ if (token != -ERESTARTSYS)+ pr_err("Failed to get the async token\n");++ return token;+ }++ rc = opal_i2c_request(token, bus_id, req);+ if (rc != OPAL_ASYNC_COMPLETION) {+ rc = -EIO;+ goto exit;+ }++ rc = opal_async_wait_response(token, &msg);+ if (rc) {+ rc = -EIO;+ goto exit;
Is it really -EIO? Maybe -ETIMEDOUT?
No, there is no timeout, if that fails something went quite wrong, it
could almost be a BUG_ON (basically we passed a wrong token or a NULL
msg).
quoted
+ }++ rc = be64_to_cpu(msg.params[1]);+ if (rc != OPAL_SUCCESS) {+ rc = -EIO;+ goto exit;+ }++exit:+ opal_async_release_token(token);+ return rc;+}++static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,+ int num)+{+ unsigned long opal_id = (unsigned long)adap->algo_data;+ struct opal_i2c_request req;+ int rc, i;++ /* We only support fairly simple combinations here of one+ * or two messages+ */
I don't think you should offer I2C_FUNC_I2C with those limitations. Is
there a case you really needs this?
Yes there is, and it's pretty common :-) I actually added this to
Neelesh original driver, it's the "smbus" style but with 2 bytes offset.
Typically what we need for driver such as at24. They use normal raw i2c
writes for writes but need the 2-bytes write + read combo without stop
for reads.
quoted
+ memset(&req, 0, sizeof(req));+ switch (num) {+ case 0:+ return 0;+ case 1:+ req.type = (msgs[0].flags & I2C_M_RD) ?+ OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;+ req.addr = cpu_to_be16(msgs[0].addr);+ req.size = cpu_to_be32(msgs[0].len);+ req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf));+ break;+ case 2:+ /* For two messages, we basically support only simple+ * smbus transactions of a write plus a read. We might+ * want to allow also two writes but we'd have to bounce+ * the data into a single buffer.+ */+ if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD))+ return -EIO;+ if (msgs[0].len > 4)+ return -EIO;+ if (msgs[0].addr != msgs[1].addr)+ return -EIO;
-EOPNOTSUPP? Please check Documentation/i2c/fault-codes for the error
codes we use.
Yes, on purpose. opal_id is u32 which is an unsigned int. You need to
cast it to an unsigned long before you cast it to a pointer or you get a
warning.
I have never seen this binding before, it looks fishy. Where is it documented?
We made it up, like pretty every SoC vendor out there. What's fishy
about it ? It's a very good way to get fixed i2c port names on the
system, the firmware defines them.
quoted
+ if (pname)+ strlcpy(adapter->name, pname, sizeof(adapter->name));+ else+ strlcpy(adapter->name, "opal", sizeof(adapter->name));++ platform_set_drvdata(pdev, adapter);+ rc = i2c_add_adapter(adapter);+ if (rc)+ dev_err(&pdev->dev, "Failed to register the i2c adapter\n");
Please put thos right below the functions it references.
quoted
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Actually, to correct myself, there are a number of error conditions
including timeouts inside the FW layer, but they are returned here, not
from opal_async_wait_response(). So indeed, we could do some error code
conversion at that point.
Neelesh, can you do that on top of your patch that adds the detailed
error codes ? We can merge it fw side tomorrow if you have a new spin,
worst case if the FW is old and only returns OPAL_HARDWARE we return
-EIO and if the FW is newer we'll have more precise error codes in Linux
too.
Cheers,
Ben.
No, there is no timeout, if that fails something went quite wrong, it
could almost be a BUG_ON (basically we passed a wrong token or a NULL
msg).
OK. I'd think it at least makes sense to use error codes which
distinguish I2C bus errors from OPAL interface errors. Always using -EIO
seems very generic :)
quoted
I don't think you should offer I2C_FUNC_I2C with those limitations. Is
there a case you really needs this?
Yes there is, and it's pretty common :-) I actually added this to
Neelesh original driver, it's the "smbus" style but with 2 bytes offset.
Typically what we need for driver such as at24. They use normal raw i2c
writes for writes but need the 2-bytes write + read combo without stop
for reads.
Understood. So, basically something like I2C_SMBUS_WORD_I2C_BLOCK_DATA
is missing where the 'command' argument is not u8 but u16? Brainstorming
here, not relevant for this driver now.
I have never seen this binding before, it looks fishy. Where is it documented?
We made it up, like pretty every SoC vendor out there. What's fishy
about it ? It's a very good way to get fixed i2c port names on the
system, the firmware defines them.
But the SoC vendors prefix it with their company name and add
documentation for the binding.
Furthermore, this is just wrong, too. The adapter name is the name of
the IP core or chip or whatever which does the I2C bus. It is not the
functional name of the bus. It should be plain "Opal I2C" or similar.
Regards,
Wolfram
Actually, to correct myself, there are a number of error conditions
including timeouts inside the FW layer, but they are returned here, not
from opal_async_wait_response(). So indeed, we could do some error code
conversion at that point.
Neelesh, can you do that on top of your patch that adds the detailed
error codes ? We can merge it fw side tomorrow if you have a new spin,
worst case if the FW is old and only returns OPAL_HARDWARE we return
-EIO and if the FW is newer we'll have more precise error codes in Linux
too.
Yes, nice to have more precise error codes so I'm adding them in firmware
and rolling out new version of the FW. Please look at it.
Thanks,
Neelesh
No, there is no timeout, if that fails something went quite wrong, it
could almost be a BUG_ON (basically we passed a wrong token or a NULL
msg).
OK. I'd think it at least makes sense to use error codes which
distinguish I2C bus errors from OPAL interface errors. Always using -EIO
seems very generic :)
Ok, any suggestion ? We don't have a -EINTERNAL :) In any case, I'm not
too worried, the above basically cannot happen.
quoted
quoted
I don't think you should offer I2C_FUNC_I2C with those limitations. Is
there a case you really needs this?
Yes there is, and it's pretty common :-) I actually added this to
Neelesh original driver, it's the "smbus" style but with 2 bytes offset.
Typically what we need for driver such as at24. They use normal raw i2c
writes for writes but need the 2-bytes write + read combo without stop
for reads.
Understood. So, basically something like I2C_SMBUS_WORD_I2C_BLOCK_DATA
is missing where the 'command' argument is not u8 but u16? Brainstorming
here, not relevant for this driver now.
Yes, or a generic "N bytes command". My FW driver supports up to 4 bytes
in fact at the moment.
I have never seen this binding before, it looks fishy. Where is it documented?
We made it up, like pretty every SoC vendor out there. What's fishy
about it ? It's a very good way to get fixed i2c port names on the
system, the firmware defines them.
But the SoC vendors prefix it with their company name and add
documentation for the binding.
Why do we need to prefix arbitrary props for a very specific device ?
When adding things to an existing more/less generic device it makes some
sense but here I don't see much point. I can whip up a "binding"
document for this adapter and make "port-name" be part of it if you
want :) In fact a better name for the property might be "bus-id"...
Furthermore, this is just wrong, too. The adapter name is the name of
the IP core or chip or whatever which does the I2C bus. It is not the
functional name of the bus. It should be plain "Opal I2C" or similar.
But that really makes no sense. On one hand we have a way to get
something decent and useful out of i2c-detect -l, and on the other hand,
we get a list of N (N = 3*number of P8 chips at least) identical names
and have to go through hoops figuring out which is which ...
I don't see why we can't use the name for that... the name scheme I use
does convey both bits of information btw, I use something like:
p8_xxxxxxxx_eypz (ex: p8_00000000_e0p0)
Where p8 means power 8 (centaur's, our memory buffers, will have
something else there), xxxxxxxx is the chip ID (identify a processor or
centaur chip uniquely in the system), y the engine number on the chip
and z the port number on the engine.
Cheers,
Ben.
I have never seen this binding before, it looks fishy. Where is it documented?
We made it up, like pretty every SoC vendor out there. What's fishy
about it ? It's a very good way to get fixed i2c port names on the
system, the firmware defines them.
But the SoC vendors prefix it with their company name and add
documentation for the binding.
Why do we need to prefix arbitrary props for a very specific device ?
When adding things to an existing more/less generic device it makes some
sense but here I don't see much point. I can whip up a "binding"
document for this adapter and make "port-name" be part of it if you
want :) In fact a better name for the property might be "bus-id"...
Note: We've slipped in a last minute update to the FW which isn't
deployed yet to turn that into ibm,port-name and change the compatible
match to the more generic "ibm,opal-i2c" since the underlying HW
implementation doesn't have to be P8 (and in fact we'll soon also expose
the i2c bus off the Centaur memory buffer chips that goes to the DIMMs).
This is the only parts of the "binding" linux cares about. The actual
DT representation for that device is a bit more complete, there's a
parent node for the i2c master engine and a node per port, but clients
like Linux only care about the ports. The clock frequencies are in
standard properties but here too, only the FW use them for its own
internal use, this is an abstract FW API (server world ....)
Cheers,
Ben.
Hi,
I am basically fine if this goes via the powerpc-tree and I was hoping
that I could ack it right now. However, the driver looks a bit rushed
and definately needs updates before it is ready to go.
On Mon, Nov 10, 2014 at 11:35:39AM +0530, Neelesh Gupta wrote:
quoted
The patch exposes the available i2c busses on the PowerNV platform
to the kernel and implements the bus driver to support i2c and
smbus commands.
The driver uses the platform device infrastructure to probe the busses
on the platform and registers them with the i2c driver framework.
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>