Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754023AbdFSLqD (ORCPT ); Mon, 19 Jun 2017 07:46:03 -0400 Received: from mail.izt-labs.de ([82.135.25.162]:59625 "EHLO mail.izt-labs.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753850AbdFSLqB (ORCPT ); Mon, 19 Jun 2017 07:46:01 -0400 X-Greylist: delayed 469 seconds by postgrey-1.27 at vger.kernel.org; Mon, 19 Jun 2017 07:46:01 EDT From: =?UTF-8?Q?Johannes_P=c3=b6hlmann?= Subject: [PATCH V1] one wire ds1wm patch Cc: Greg Kroah-Hartman , Evgeniy Polyakov To: linux-kernel@vger.kernel.org Message-ID: <6a03463f-7e93-8ee7-d56f-aaed1e2befba@izt-labs.de> Date: Mon, 19 Jun 2017 13:38:00 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.6.0 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------120F29D7DFEAF869264DD41B" Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11509 Lines: 383 This is a multi-part message in MIME format. --------------120F29D7DFEAF869264DD41B Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable To make the ds1wm driver work on a powerpc architecture (big endian, 32bi= t) with a register offset multiplier of 4 i had to make some changes to drivers/w1/masters/ds1wm.c and include/linux/mfd/ds1wm.h. I grouped theses into 4 patches of falling priority. [PATCH 1/4] fix and simplify register access [PATCH 2/4] Add level interrupt modes (maybe no longer needed in newer kernels) [PATCH 3/4] Silence interrupts on HW before claiming the interrupt [PATCH 4/4] optional: add messages to make incorporation in mfd drivers easier The patches applied cleanly against commit 32c1431eea4881a6b17bd7c639315010aeefa452 Author: Linus Torvalds Date: Sun Jun 11 16:48:20 2017 -0700 Linux 4.12-rc5 I could build the patched kernel (as above) with ds1wm configured in with no errors or warnings. I could test and verify the correct working of the patch against kernel 3.12.15, but not against the current kernel. --------------120F29D7DFEAF869264DD41B Content-Type: text/x-patch; name="0001-fix-and-simplify-register-access.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="0001-fix-and-simplify-register-access.patch" =46rom 6edd0fa88631cc20a742e0567c2e0e6cb9e5a8fc Mon Sep 17 00:00:00 2001 From: Johannes Poeehlmann Date: Thu, 9 Feb 2017 14:05:22 +0100 Subject: [PATCH 1/4] fix and simplify register access o Replace incorrect register offsett calculation by direct configuration of bus_shift in mfd-cell. Indirect definition of address-shift by resource size was unobvious and should have used a binary log. o Make endian clean, make HW-endianness configurable. o Use ioread*, iowrite* instead of __raw_readb,__raw_writeb to also use memory-barriers when accessing HW-registers. We do not want reordering to happen here. Signed-off-by: Johannes Poeehlmann --- drivers/w1/masters/ds1wm.c | 92 ++++++++++++++++++++++++++++++++++++++++= +++--- include/linux/mfd/ds1wm.h | 10 +++++ 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index e0b8a4b..e6284b7 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -97,6 +97,7 @@ static struct { struct ds1wm_data { void __iomem *map; int bus_shift; /* # of shifts to calc register offsets */ + int isHwBigEndian; struct platform_device *pdev; const struct mfd_cell *cell; int irq; @@ -116,12 +117,83 @@ struct ds1wm_data { static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u= 32 reg, u8 val) { - __raw_writeb(val, ds1wm_data->map + (reg << ds1wm_data->bus_shift)); + if (ds1wm_data->isHwBigEndian) { + switch (ds1wm_data->bus_shift) { + case BUSWIDTH8: + writeb(val, ds1wm_data->map + (reg << BUSWIDTH8)); + break; + case BUSWIDTH16: + writew_be((u16)val, ds1wm_data->map+(reg<map+(reg<pdev->dev, + "illegal bus shift %d, not written", + ds1wm_data->bus_shift); + } + } else { + switch (ds1wm_data->bus_shift) { + case BUSWIDTH8: + writeb(val, ds1wm_data->map + (reg << BUSWIDTH8)); + break; + case BUSWIDTH16: + writew((u16) val, ds1wm_data->map+(reg << BUSWIDTH16)); + break; + case BUSWIDTH32: + writel((u32) val, ds1wm_data->map+(reg << BUSWIDTH32)); + break; + default: + dev_err(&ds1wm_data->pdev->dev, + "illegal bus shift %d, not written", + ds1wm_data->bus_shift); + } + } } =20 static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 = reg) { - return __raw_readb(ds1wm_data->map + (reg << ds1wm_data->bus_shift)); + + u32 val =3D 0; + if (ds1wm_data->isHwBigEndian) { + switch (ds1wm_data->bus_shift) { + case BUSWIDTH8: + val =3D readb(ds1wm_data->map + (reg << BUSWIDTH8)); + break; + case BUSWIDTH16: + val =3D readw_be(ds1wm_data->map + (reg << BUSWIDTH16)); + break; + case BUSWIDTH32: + val =3D readl_be(ds1wm_data->map + (reg << BUSWIDTH32)); + break; + default: + dev_err(&ds1wm_data->pdev->dev, + "illegal bus shift %d, not read", + ds1wm_data->bus_shift); + } + } else { + switch (ds1wm_data->bus_shift) { + case BUSWIDTH8: + val =3D readb(ds1wm_data->map + (reg << BUSWIDTH8)); + break; + case BUSWIDTH16: + val =3D readw(ds1wm_data->map + (reg << BUSWIDTH16)); + break; + case BUSWIDTH32: + val =3D readl(ds1wm_data->map + (reg << BUSWIDTH32)); + break; + default: + dev_err(&ds1wm_data->pdev->dev, + "illegal bus shift %d, not read", + ds1wm_data->bus_shift); + + return 0; + } + } + dev_dbg(&ds1wm_data->pdev->dev, + "ds1wm_read_register reg: %d, 32 bit val:%x\n", reg, val); + return (u8) val; } =20 =20 @@ -474,9 +546,6 @@ static int ds1wm_probe(struct platform_device *pdev) if (!ds1wm_data->map) return -ENOMEM; =20 - /* calculate bus shift from mem resource */ - ds1wm_data->bus_shift =3D resource_size(res) >> 3; - ds1wm_data->pdev =3D pdev; ds1wm_data->cell =3D mfd_get_cell(pdev); if (!ds1wm_data->cell) @@ -485,6 +554,19 @@ static int ds1wm_probe(struct platform_device *pdev)= if (!plat) return -ENODEV; =20 + /* how many bits to shift register number to get register offset */ + ds1wm_data->bus_shift =3D plat->bus_shift; + + /* make sure resource has space for 8 registers */ + if ( (8 << ds1wm_data->bus_shift ) > resource_size(res) ){ + dev_err(&ds1wm_data->pdev->dev, + "memory ressource size %d to small, should be %d\n", + (int) resource_size(res), + 8 << ds1wm_data->bus_shift); + } + + ds1wm_data->isHwBigEndian =3D plat->isHwBigEndian; + res =3D platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) return -ENXIO; diff --git a/include/linux/mfd/ds1wm.h b/include/linux/mfd/ds1wm.h index 38a372a..2fbc433 100644 --- a/include/linux/mfd/ds1wm.h +++ b/include/linux/mfd/ds1wm.h @@ -1,5 +1,10 @@ /* MFD cell driver data for the DS1WM driver */ =20 +/* bus shift constants */ +#define BUSWIDTH8 0 +#define BUSWIDTH16 1 +#define BUSWIDTH32 2 + struct ds1wm_driver_data { int active_high; int clock_rate; @@ -10,4 +15,9 @@ struct ds1wm_driver_data { /* ds1wm implements the precise timings of*/ /* a reset pulse/presence detect sequence.*/ unsigned int reset_recover_delay; + /* Say 1 here for big endian Hardware + * (only relevant with bus-shift > 0*/ + int isHwBigEndian; + /* left shift of register number to get register address offsett */ + int bus_shift; }; --=20 2.1.4 --------------120F29D7DFEAF869264DD41B Content-Type: text/x-patch; name="0002-Add-level-interrupt-modes-maybe-no-longer-needed-in-.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename*0="0002-Add-level-interrupt-modes-maybe-no-longer-needed-in-.pa"; filename*1="tch" =46rom b7dffcbde0fb81fcbf4fb6ac7b54e59ec9064d27 Mon Sep 17 00:00:00 2001 From: Johannes Poeehlmann Date: Thu, 9 Feb 2017 14:06:08 +0100 Subject: [PATCH 2/4] Add level interrupt modes (maybe no longer needed in= newer kernels) Signed-off-by: Johannes Poeehlmann --- drivers/w1/masters/ds1wm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index e6284b7..bed512b2 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -578,6 +578,10 @@ static int ds1wm_probe(struct platform_device *pdev)= irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING); if (res->flags & IORESOURCE_IRQ_LOWEDGE) irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); + if (res->flags & IORESOURCE_IRQ_HIGHLEVEL) + irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_LEVEL_HIGH); + if (res->flags & IORESOURCE_IRQ_LOWLEVEL) + irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_LEVEL_LOW); =20 ret =3D devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, IRQF_SHARED, "ds1wm", ds1wm_data); --=20 2.1.4 --------------120F29D7DFEAF869264DD41B Content-Type: text/x-patch; name="0003-Silence-interrupts-on-HW-before-claiming-the-interru.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename*0="0003-Silence-interrupts-on-HW-before-claiming-the-interru.pa"; filename*1="tch" =46rom 1f29d55030a65e2223ea8bea9d18889a34770210 Mon Sep 17 00:00:00 2001 From: Johannes Poeehlmann Date: Thu, 9 Feb 2017 14:07:10 +0100 Subject: [PATCH 3/4] Silence interrupts on HW before claiming the interru= pt --- drivers/w1/masters/ds1wm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index bed512b2..bdfb19c 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -528,6 +528,7 @@ static int ds1wm_probe(struct platform_device *pdev) struct ds1wm_driver_data *plat; struct resource *res; int ret; + u8 inten; =20 if (!pdev) return -ENODEV; @@ -574,6 +575,11 @@ static int ds1wm_probe(struct platform_device *pdev)= ds1wm_data->int_en_reg_none =3D (plat->active_high ? DS1WM_INTEN_IAS : = 0); ds1wm_data->reset_recover_delay =3D plat->reset_recover_delay; =20 + /* Mask interrupts, set IAS before claiming interrupt */ + inten =3D ds1wm_read_register(ds1wm_data, DS1WM_INT_EN); + ds1wm_write_register(ds1wm_data, + DS1WM_INT_EN, ds1wm_data->int_en_reg_none); + if (res->flags & IORESOURCE_IRQ_HIGHEDGE) irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING); if (res->flags & IORESOURCE_IRQ_LOWEDGE) --=20 2.1.4 --------------120F29D7DFEAF869264DD41B Content-Type: text/x-patch; name="0004-optional-add-messages-to-make-incorporation-in-mfd-d.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename*0="0004-optional-add-messages-to-make-incorporation-in-mfd-d.pa"; filename*1="tch" =46rom 49457b8f75bd169d1cc511355341ce459ac9095a Mon Sep 17 00:00:00 2001 From: Johannes Poeehlmann Date: Thu, 9 Feb 2017 14:09:56 +0100 Subject: [PATCH 4/4] optional: add messages to make incorporation in mfd-drivers easier --- drivers/w1/masters/ds1wm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index bdfb19c..d480c27 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -591,8 +591,14 @@ static int ds1wm_probe(struct platform_device *pdev)= =20 ret =3D devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, IRQF_SHARED, "ds1wm", ds1wm_data); - if (ret) + if (ret){ + dev_err(&ds1wm_data->pdev->dev, + "devm_request_irq %d failed with errno %d\n", + ds1wm_data->irq, + ret); + return ret; + } =20 ds1wm_up(ds1wm_data); =20 @@ -602,6 +608,14 @@ static int ds1wm_probe(struct platform_device *pdev)= if (ret) goto err; =20 + dev_info(&ds1wm_data->pdev->dev, + "ds1wm: probe successful, IAS: %d, rec.delay: %d, " + "clockrate: %d, bus-shift: %d, is Hw Big Endian: %d\n", + plat->active_high, + plat->reset_recover_delay, + plat->clock_rate, + ds1wm_data->bus_shift, + ds1wm_data->isHwBigEndian); return 0; =20 err: --=20 2.1.4 --------------120F29D7DFEAF869264DD41B--