From patchwork Mon Jul 6 12:38:28 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Jorge_S=C3=A1nchez_de_Nova?= X-Patchwork-Id: 29497 X-Patchwork-Delegate: grant.likely@secretlab.ca Return-Path: X-Original-To: patchwork-incoming@bilbo.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from ozlabs.org (ozlabs.org [203.10.76.45]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "mx.ozlabs.org", Issuer "CA Cert Signing Authority" (verified OK)) by bilbo.ozlabs.org (Postfix) with ESMTPS id 5049FB7086 for ; Mon, 6 Jul 2009 22:47:43 +1000 (EST) Received: by ozlabs.org (Postfix) id 415E5DDD1C; Mon, 6 Jul 2009 22:47:43 +1000 (EST) Delivered-To: patchwork-incoming@ozlabs.org Received: from bilbo.ozlabs.org (bilbo.ozlabs.org [203.10.76.25]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "bilbo.ozlabs.org", Issuer "CAcert Class 3 Root" (verified OK)) by ozlabs.org (Postfix) with ESMTPS id 361C0DDD1B for ; Mon, 6 Jul 2009 22:47:43 +1000 (EST) Received: from bilbo.ozlabs.org (localhost [127.0.0.1]) by bilbo.ozlabs.org (Postfix) with ESMTP id 47204B7321 for ; Mon, 6 Jul 2009 22:46:45 +1000 (EST) Received: from ey-out-2122.google.com (ey-out-2122.google.com [74.125.78.26]) by bilbo.ozlabs.org (Postfix) with ESMTP id 67F06B707D for ; Mon, 6 Jul 2009 22:38:31 +1000 (EST) Received: by ey-out-2122.google.com with SMTP id 9so958724eyd.27 for ; Mon, 06 Jul 2009 05:38:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:cc:content-type; bh=l9vrX3Nw0IxaHBAuonqLwQApFSJuCdtEYNAVyZ3O/r0=; b=ORUuDSNhgrGUyXkmSTF7k9djhqZ73CqhO+dLbTRKhw1CCVmZe36xbM8Ai0u1D1M6yo Vv0zsShXr/+pUJFmcw2/HSwvNHShYsftS2ak6Y7+0YKHdlXOInGM6wLN1fqQHSHvN/fj 99d0xJaYlGNgtiu5t14npfAN8Q4EcqOl+rPUg= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; b=S/+ukwdzHzIRiZEq8HfewWj9qRCK7AdyyKjsTXsZPpjylOFBN0gf4r7sY1BgUvvfiT H67+TPyhLk5w48EukINaclcsmHInQlSBaj23swn9IIM63itP4HuMmFLo6SfBLuU3QkX5 jglr1+9TGcRVmGxtLyvRKAiEbmxVrdSyK8jt4= MIME-Version: 1.0 Received: by 10.210.116.14 with SMTP id o14mr5456990ebc.97.1246883908368; Mon, 06 Jul 2009 05:38:28 -0700 (PDT) In-Reply-To: <871vp2eu09.fsf@macbook.be.48ers.dk> References: <621779fd0906290825i6bdc1e99tf6be68e3de56e9a6@mail.gmail.com> <871vp2eu09.fsf@macbook.be.48ers.dk> Date: Mon, 6 Jul 2009 14:38:28 +0200 Message-ID: <621779fd0907060538x705e5888ke55ff642abe254f@mail.gmail.com> Subject: Re: Device tree for c67x00 From: =?ISO-8859-1?Q?Jorge_S=E1nchez_de_Nova?= To: Peter Korsgaard X-Mailman-Approved-At: Mon, 06 Jul 2009 22:46:37 +1000 Cc: Linuxppc-dev@lists.ozlabs.org X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Hi, ok, so I have managed to give it some OF support in a preliminary way(see patch). But now, I am facing serious problems which I am finding difficult to tackle. I understand that maybe these problems have to do partially or entirely to the Xilinx ML403. Let me explain myself: First of all, I don't know of anyone else who has used it for the ML403 with the XPS_EPC , so there might be some particularities I should specifically take care of. First of all, I see a resetting problem. When I load the kernel with my board from a powered off state to a powered on state it behaves differently that if I just to try to load the kernel again with the board powered on (see ERROR 1 below). I have seen that the reset line in the c67300 is connected to the fpga and for sure, to take care of it one would need to do it through a third gpio line outside. This would be to make a hard reset. Looking at this old specific driver that I have found for kernel 2.4.20 ( http://www.sysf.physto.se/~attila/ATLAS/Digitizer/Testbench/System_ISE_SoftMAC/linux/software/uClinux-2.4.x/drivers/usb/cy7c67300/) I see that the hard resets are common using the ugly gpio method. With the board on, if I reload the fpga programming and kernel image into memory and set the PC to run properly, it gives different errors. Sometimes ERROR2 and some other times ERROR3. When ERROR2, the processor halts when calling WARN_ON(!res) and in the second, the interrupt handler keeps writing to the output endlessly. I have added some printks to trace the problem and the OF support is simply a rewriting of the normal platform_driver registration. Any ideas? Jorge -- ERROR 1 (just after power on-- Generic platform RAM MTD, (c) 2004 Simtec Electronics usbmon: debugfs is not available C67X00_DBG:c67x00_of_probe() - Request memory region C67X00_DBG:c67x00_of_probe() - Allocating data structs C67X00_DBG:c67x00_of_probe() - Configuring c67x00 device C67X00_DBG:c67x00_of_probe() - Configure platform data based on the device-tree data C67X00_DBG:c67x00_of_probe() - hpi.regstep: 2 C67X00_DBG:c67x00_of_probe() - SIE_config: 1 C67X00_DBG:c67x00_of_probe() - SIE_config: 21 C67X00_DBG:c67x00_of_probe() - Low-level initizalization C67X00_DBG:c67x00_ll_hpi_reg_init(): Reg:324 written=0x00 ; read: 0 C67X00_DBG:c67x00_ll_hpi_reg_init(): Reg:328 written=0x00 ; read: 0 C67X00_DBG:c67x00_of_probe() - Registering IRQ C67X00_DBG:c67x00_of_probe() - Trying to register IRQ:17 @ 17 C67X00_DBG:c67x00_irq() - Handling IRQ number 57005 Å��Å���;�L�=?� : Not all interrupts handled! status = 0xdead C67X00_DBG:c67x00_irq() - Handling IRQ number 57005 C67X00_DBG:c67x00_of_probe() - Low-level reset C67X00_DBG:c67x00_ll_reset() - Send mbox C67X00_DBG:c67x00_ll_reset() - recv_msg C67X00_DBG:ll_recv_msg() - calling wait_for_completion_timeout C67X00_DBG:ll_recv_msg() res=5000 C67X00_DBG:c67x00_ll_reset() - done recv_msg C67X00_DBG:c67x00_of_probe() - Configuring SIEs C67X00_DBG:ll_recv_msg() - calling wait_for_completion_timeout C67X00_DBG:ll_recv_msg() res=5000 Å��Å��Ø� : SIE 0 not set to host mode Å��Å��Ø� : Cypress C67X00 Host Controller Unable to handle kernel paging request for data at address 0x00000002 Faulting instruction address: 0xc0012654 Oops: Kernel access of bad area, sig: 11 [#1] PREEMPT Xilinx Virtex Modules linked in: NIP: c0012654 LR: c01c8ca0 CTR: c01fa0e4 REGS: c381db90 TRAP: 0300 Not tainted (2.6.29.4) MSR: 00029030 CR: 35039055 XER: a000004b DEAR: 00000002, ESR: 00000000 TASK = c381a000[1] 'swapper' THREAD: c381c000 GPR00: 00000002 c381dc40 c381a000 00000002 00000001 00000000 fffffffe ffffffff GPR08: 00000000 00000008 00000003 c380a0b1 35039059 ffffffff ffffffff ffffffff GPR16: ffffffff ffffffff ffffffff ffffffff ffffffff c398fee0 c035bdc4 c0360acc GPR24: 00000000 00000000 c380a000 c03cac98 00000001 c385a84c 000000d0 c385a84c NIP [c0012654] strlen+0x4/0x18 LR [c01c8ca0] kobject_get_path+0x34/0xe0 Call Trace: [c381dc40] [c01c91fc] add_uevent_var+0x74/0xf4 (unreliable) [c381dc60] [c01fa190] dev_uevent+0xac/0x210 [c381dc80] [c01c94a8] kobject_uevent_env+0x22c/0x454 [c381dcd0] [c01fb164] device_add+0x39c/0x594 [c381dd20] [c01fb418] device_create_vargs+0x8c/0xd0 [c381dd50] [c01fb49c] device_create+0x40/0x50 [c381dd80] [c0219fc0] usb_add_hcd+0x140/0x6d4 [c381ddb0] [c022daac] c67x00_hcd_probe+0x120/0x1f0 [c381ddd0] [c022c88c] c67x00_probe_sie+0xcc/0xe0 [c381dde0] [c02f43a4] c67x00_of_probe+0x3fc/0x458 [c381de50] [c0258a88] of_platform_device_probe+0x5c/0x84 [c381de70] [c01fdb8c] driver_probe_device+0xbc/0x1f4 [c381de90] [c01fdd68] __driver_attach+0xa4/0xa8 [c381deb0] [c01fce78] bus_for_each_dev+0x5c/0x98 [c381dee0] [c01fd990] driver_attach+0x24/0x34 [c381def0] [c01fd798] bus_add_driver+0x1d0/0x250 [c381df20] [c01fe0b4] driver_register+0x5c/0x150 [c381df40] [c0258950] of_register_driver+0x54/0x70 [c381df50] [c03aaa8c] c67x00_init+0x34/0x8c [c381df60] [c000236c] do_one_initcall+0x34/0x1a4 [c381dfd0] [c0393170] kernel_init+0x90/0xfc [c381dff0] [c000fa24] kernel_thread+0x4c/0x68 Instruction dump: 4d820020 7ca903a6 38a3ffff 3884ffff 8c650001 2c830000 8c040001 7c601851 4d860020 4102ffec 4e800020 3883ffff <8c040001> 2c000000 4082fff8 7c632050 ---[ end trace f8500dd73d54b5fd ]--- Kernel panic - not syncing: Attempted to kill init! Rebooting in 180 seconds.. -- ERROR 2 (with the board already on, just reprogramming fpga, kernel and booting again)-- Generic platform RAM MTD, (c) 2004 Simtec Electronics usbmon: debugfs is not available C67X00_DBG:c67x00_of_probe() - Request memory region C67X00_DBG:c67x00_of_probe() - Allocating data structs C67X00_DBG:c67x00_of_probe() - Configuring c67x00 device C67X00_DBG:c67x00_of_probe() - Configure platform data based on the device-tree data C67X00_DBG:c67x00_of_probe() - hpi.regstep: 2 C67X00_DBG:c67x00_of_probe() - SIE_config: 1 C67X00_DBG:c67x00_of_probe() - SIE_config: 21 C67X00_DBG:c67x00_of_probe() - Low-level initizalization C67X00_DBG:c67x00_ll_hpi_reg_init(): Reg:324 written=0x00 ; read: 0 C67X00_DBG:c67x00_ll_hpi_reg_init(): Reg:328 written=0x00 ; read: 0 C67X00_DBG:c67x00_of_probe() - Registering IRQ C67X00_DBG:c67x00_of_probe() - Trying to register IRQ:17 @ 17 C67X00_DBG:c67x00_of_probe() - Low-level reset C67X00_DBG:c67x00_ll_reset() - Send mbox C67X00_DBG:c67x00_ll_reset() - recv_msg C67X00_DBG:ll_recv_msg() - calling wait_for_completion_timeout C67X00_DBG:ll_recv_msg() res=0 --> Here the processor halts when in call to WARN_ON(!res); -- ERROR 3: gets stuck in the interrupt handler -- Å��Å���;�L�=?� : Not all interrupts handled! status = 0x0148 C67X00_DBG:c67x00_irq() - Handling IRQ number 328 2009/6/29 Peter Korsgaard > >>>>> "Jorge" == Jorge Sánchez de Nova writes: > > Jorge> Hi, > > Jorge> It doesn't work at all since it doesn't load anything. I have > Jorge> looked at the driver and there is apparently no openfirmware > Jorge> support for it, so maybe the dts info won't work without > Jorge> it. Am I wrong? Does this means that the c67x00 needs OF > Jorge> support to work in this configuration? How can I make it > Jorge> otherwise? > > Yes, the c67x00 driver doesn't currently have any OF bindings. Either > you can add it, or simply manually create the struct platform_device > in your board file (or scan the DT in your board file and fill in the > correct base address / interrupt number from it). > > Remember that arch/powerpc uses virtual interrupt numbers if you're > going to fill in the platform_device by hand. > > -- > Bye, Peter Korsgaard > diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index 5633bc5..699cbef 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -39,6 +39,10 @@ #include #include #include +#if defined(CONFIG_OF) +#include +#include +#endif #include "c67x00.h" #include "c67x00-hcd.h" @@ -87,8 +91,9 @@ static irqreturn_t c67x00_irq(int irq, void *__dev) struct c67x00_sie *sie; u16 msg, int_status; int i, count = 8; - +// printk("Handling IRQ!\n"); int_status = c67x00_ll_hpi_status(c67x00); + printk("C67X00_DBG:c67x00_irq() - Handling IRQ number %d\n",int_status); if (!int_status) return IRQ_NONE; @@ -225,14 +230,241 @@ static struct platform_driver c67x00_driver = { }; MODULE_ALIAS("platform:c67x00"); +/* --------------------------------------------------------------------- + * OF_Platform Bus Support + */ + +#if defined(CONFIG_OF) +static int __devinit c67x00_of_probe(struct of_device *dev, + const struct of_device_id *match) +{ + struct c67x00_device *c67x00; + struct c67x00_platform_data *pdata; + struct resource r_irq_struct; + struct resource *res; + struct resource memory; + struct device_node *dp = dev->node; + struct resource* r_irq = &r_irq_struct; + u32* iprop; + int len, ret, i; + + /* Request memory region */ + printk("C67X00_DBG:c67x00_of_probe() - Request memory region\n"); + ret = of_address_to_resource(dp, 0, &memory); + if (ret) + return -ENXIO; + + res = request_mem_region(memory.start, memory.end - memory.start + 1, + dev_name(&dev->dev)); + + if (!res){ + dev_err(&dev->dev, "Memory region busy\n"); + return -EBUSY; + } + + /* Allocate device and platform_data structs */ + printk("C67X00_DBG:c67x00_of_probe() - Allocating data structs\n"); + c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL); + if (!c67x00) + return -ENOMEM; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata){ + ret = -ENOMEM; + goto request_mem_failed; + } + + /* Configure c67x00 device */ + printk("C67X00_DBG:c67x00_of_probe() - Configuring c67x00 device\n"); + c67x00->hpi.base = ioremap(memory.start, memory.end - memory.start + 1); + + if (!c67x00->hpi.base) { + dev_err(&dev->dev, "Unable to map HPI registers\n"); + ret = -EIO; + goto map_failed; + } + + /* Configure platform data based on the device-tree data */ + printk("C67X00_DBG:c67x00_of_probe() - Configure platform data based on the device-tree data\n"); + spin_lock_init(&c67x00->hpi.lock); + //TODO: Check that we are using the ML403 + + iprop = of_get_property(dev->node, "xlnx,hpi_regstep", &len); + if (!iprop || len < sizeof(*iprop)) { + dev_err(&dev->dev, "No 'xlnx,hpi_regstep property'\n"); + ret = -EIO; + goto map_failed; //TODO + } + c67x00->hpi.regstep = *iprop; + printk("C67X00_DBG:c67x00_of_probe() - hpi.regstep: %x\n", c67x00->hpi.regstep); + + iprop = of_get_property(dev->node, "xlnx,sie1_config", &len); + if (!iprop || len < sizeof(*iprop)) { + dev_err(&dev->dev, "No 'xlnx,sie1_config'\n"); + ret = -EIO; + goto map_failed; //TODO + } + pdata->sie_config = *iprop; + printk("C67X00_DBG:c67x00_of_probe() - SIE_config: %x\n", pdata->sie_config); + + iprop = of_get_property(dev->node, "xlnx,sie2_config", &len); + if (!iprop || len < sizeof(*iprop)) { + dev_err(&dev->dev, "No 'xlnx,sie2_config'\n"); + ret = -EIO; + goto map_failed; //TODO + } + pdata->sie_config |= *iprop << 4; + printk("C67X00_DBG:c67x00_of_probe() - SIE_config: %x\n", pdata->sie_config); + + c67x00->pdata = pdata; + c67x00->pdev = dev; + + /* Low-level initizalization */ + printk("C67X00_DBG:c67x00_of_probe() - Low-level initizalization \n"); + c67x00_ll_init(c67x00); + c67x00_ll_hpi_reg_init(c67x00); + + /* Request IRQ */ + printk("C67X00_DBG:c67x00_of_probe() - Registering IRQ\n"); + dev_dbg(&dev->dev, "Registering IRQ\n"); + ret = of_irq_to_resource(dev->node, 0, r_irq); + + if (ret == NO_IRQ) { + dev_err(&dev->dev, "Couldn't find IRQ in device-tree\n"); + goto request_irq_failed; + } + printk("C67X00_DBG:c67x00_of_probe() - Trying to register IRQ:%d @ %d\n",ret,r_irq->start); + ret = request_irq(r_irq->start, c67x00_irq, 0, dev_name(&dev->dev), c67x00); + if (ret) { + dev_err(&dev->dev, "Cannot claim IRQ\n"); + goto request_irq_failed; + } + + /* Reset c67x00 as part of the initizalization*/ + printk("C67X00_DBG:c67x00_of_probe() - Low-level reset\n"); + ret = c67x00_ll_reset(c67x00); + if (ret) { + printk("Device reset failed\n"); + dev_err(&dev->dev, "Device reset failed\n"); + goto reset_failed; + } + + /* Probe each of the SIEs */ + printk("C67X00_DBG:c67x00_of_probe() - Configuring SIEs\n"); + dev_dbg(&dev->dev, "Configuring SIEs\n"); + for (i = 0; i < C67X00_SIES; i++) + c67x00_probe_sie(&c67x00->sie[i], c67x00, i); + printk("C67X00_DBG:c67x00_of_probe() - Saving driver data\n"); + dev_set_drvdata(&dev->dev, c67x00); + return 0; + + reset_failed: + free_irq(r_irq->start, c67x00); + request_irq_failed: + iounmap(c67x00->hpi.base); + map_failed: + release_mem_region(memory.start, memory.end - memory.start + 1); + kfree(pdata); + request_mem_failed: + kfree(c67x00); + + return ret; +} + +static int __devexit c67x00_of_remove(struct of_device *dev) +{ + struct c67x00_device *c67x00 = dev_get_drvdata(dev); + struct resource *res; + int i; + + dev_dbg(dev, "c67x00_free(%p)\n", dev); + + for (i = 0; i < C67X00_SIES; i++) + c67x00_remove_sie(&c67x00->sie[i]); + + c67x00_ll_release(c67x00); + + //TODO: release IRQ? +// res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +// if (res) +// free_irq(res->start, c67x00); + + iounmap(c67x00->hpi.base); + + +// res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +// if (res) +// release_mem_region(res->start, res->end - res->start + 1); + + kfree(c67x00); + + return 0; +} + +/* Match table for of_platform binding */ +static struct of_device_id c67x00_of_match[] __devinitdata = { + { .compatible = "cy,c67x00", }, + { .compatible = "cy,c67300", }, + {}, +}; +MODULE_DEVICE_TABLE(of, c67x00_of_match); + +static struct of_platform_driver c67x00_of_driver = { + .owner = THIS_MODULE, + .name = "c67x00", + .match_table = c67x00_of_match, + .probe = c67x00_of_probe, + .remove = __devexit_p(c67x00_of_remove), + .driver = { + .name = "c67x00", + .owner = THIS_MODULE, + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __init c67x00_of_register(void) +{ + pr_debug("c67x00: registering OF binding\n"); + return of_register_platform_driver(&c67x00_of_driver); +} + +static inline void __exit c67x00_of_unregister(void) +{ + of_unregister_platform_driver(&c67x00_of_driver); +} +#else /* CONFIG_OF */ +/* CONFIG_OF not enabled; do nothing helpers */ +static inline int __init c67x00_of_register(void) { return 0; } +static inline void __exit c67x00_of_unregister(void) { } +#endif /* CONFIG_OF */ + static int __init c67x00_init(void) { - return platform_driver_register(&c67x00_driver); + int rc; + + rc = c67x00_of_register(); + if (rc) + goto err_of; + + pr_debug("c67x00: registering platform binding\n"); + rc = platform_driver_register(&c67x00_driver); + if (rc) + goto err_plat; + + return 0; + +err_of: + c67x00_of_unregister(); +err_plat: + printk("c67x00: registration failed; err:%i\n",rc); + return rc; } static void __exit c67x00_exit(void) { + pr_debug("Unregistering c67x00\n"); platform_driver_unregister(&c67x00_driver); + c67x00_of_unregister(); } module_init(c67x00_init); diff --git a/drivers/usb/c67x00/c67x00-ll-hpi.c b/drivers/usb/c67x00/c67x00-ll-hpi.c index a9636f4..2e7061a 100644 --- a/drivers/usb/c67x00/c67x00-ll-hpi.c +++ b/drivers/usb/c67x00/c67x00-ll-hpi.c @@ -219,6 +219,7 @@ void c67x00_ll_hpi_reg_init(struct c67x00_device *dev) for (i = 0; i < C67X00_SIES; i++) { hpi_write_word(dev, SIEMSG_REG(i), 0); hpi_read_word(dev, SIEMSG_REG(i)); + printk("C67X00_DBG:c67x00_ll_hpi_reg_init(): Reg:%d written=0x00 ; read: %d\n",SIEMSG_REG(i)); } } @@ -240,8 +241,9 @@ void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie) static inline u16 ll_recv_msg(struct c67x00_device *dev) { u16 res; - + printk("C67X00_DBG:ll_recv_msg() - calling wait_for_completion_timeout \n"); res = wait_for_completion_timeout(&dev->hpi.lcp.msg_received, 5 * HZ); + printk("C67X00_DBG:ll_recv_msg() res=%d\n", res); WARN_ON(!res); return (res == 0) ? -EIO : 0; @@ -388,10 +390,13 @@ void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status) int c67x00_ll_reset(struct c67x00_device *dev) { int rc; - + mutex_lock(&dev->hpi.lcp.mutex); + printk("C67X00_DBG:c67x00_ll_reset() - Send mbox\n"); hpi_send_mbox(dev, COMM_RESET); + printk("C67X00_DBG:c67x00_ll_reset() - recv_msg\n"); rc = ll_recv_msg(dev); + printk("C67X00_DBG:c67x00_ll_reset() - done recv_msg\n"); mutex_unlock(&dev->hpi.lcp.mutex); return rc;