* [PATCH v3 2/3] i2c: acpi: Initialize info.type from of_compatible
From: Dan O'Donovan @ 2017-01-23 16:08 UTC (permalink / raw)
To: linux-acpi, Rafael J . Wysocki, Jarkko Nikula, Mika Westerberg,
Mark Brown
Cc: Len Brown, linux-i2c, Wolfram Sang, linux-spi, linux-kernel,
Dan O'Donovan, Crestez Dan Leonard
In-Reply-To: <1485187737-22414-1-git-send-email-dan@emutex.com>
From: Crestez Dan Leonard <leonard.crestez@intel.com>
When using devicetree i2c_board_info.type is set to the compatible
string with the vendor prefix removed. For I2C devices described via
ACPI the i2c_board_info.type string is set to the ACPI device name. When
using ACPI and DT ids this string ends up something like "PRP0001:00".
If the of_compatible property is present try to use that instead. This
makes it easier to instantiate i2c drivers through ACPI with DT ids.
Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
Signed-off-by: Dan O'Donovan <dan@emutex.com>
---
drivers/i2c/i2c-core.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 583e950..a83dbc7 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -221,7 +221,13 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
acpi_dev_free_resource_list(&resource_list);
- strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type));
+ /*
+ * Populate modalias from compatible property if available,
+ * otherwise use native ACPI information
+ */
+ if ((!adev->data.of_compatible) ||
+ acpi_of_modalias(adev, info->type, sizeof(info->type)))
+ strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type));
return 0;
}
--
2.7.4
^ permalink raw reply related
* [PATCH v3 1/3] ACPI / bus: Export acpi_of_modalias equiv of of_modalias_node
From: Dan O'Donovan @ 2017-01-23 16:08 UTC (permalink / raw)
To: linux-acpi-u79uwXL29TY76Z2rM5mHXA, Rafael J . Wysocki,
Jarkko Nikula, Mika Westerberg, Mark Brown
Cc: Len Brown, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Wolfram Sang,
linux-spi-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Dan O'Donovan,
Crestez Dan Leonard
In-Reply-To: <1485187737-22414-1-git-send-email-dan-M3NBUjLqch7QT0dZR+AlfA@public.gmane.org>
From: Crestez Dan Leonard <leonard.crestez-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
When using devicetree stuff like i2c_client.name or spi_device.modalias
is initialized to the first DT compatible id with the vendor prefix
stripped. Since some drivers rely on this try to replicate it when using
ACPI with DT ids.
Signed-off-by: Crestez Dan Leonard <leonard.crestez-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Signed-off-by: Dan O'Donovan <dan-M3NBUjLqch7QT0dZR+AlfA@public.gmane.org>
Reviewed-by: Mika Westerberg <mika.westerberg-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
drivers/acpi/bus.c | 35 +++++++++++++++++++++++++++++++++++
include/acpi/acpi_bus.h | 1 +
2 files changed, 36 insertions(+)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 95855cb..8b9657f 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -677,6 +677,41 @@ static bool acpi_of_match_device(struct acpi_device *adev,
return false;
}
+/**
+ * acpi_of_modalias - Like of_modalias_node for ACPI with DT ids
+ * @adev: ACPI device object to match.
+ * @outstr: Pointer to buffer for result
+ * @outlen: Length of outstr value
+ *
+ * This is a counterpart of of_modalias_node() for struct acpi_device
+ * objects. If there is a compatible string for @adev, copy it to the
+ * @outstr location with the vendor prefix stripped.
+ *
+ * Returns 0 on success or negative errno on failure.
+ */
+int acpi_of_modalias(struct acpi_device *adev, char *outstr, size_t outlen)
+{
+ const union acpi_object *of_compatible;
+ const union acpi_object *obj;
+ const char *str, *chr;
+
+ of_compatible = adev->data.of_compatible;
+ if (!of_compatible)
+ return -ENODEV;
+
+ if (of_compatible->type == ACPI_TYPE_PACKAGE)
+ obj = of_compatible->package.elements;
+ else /* Must be ACPI_TYPE_STRING. */
+ obj = of_compatible;
+
+ str = obj->string.pointer;
+ chr = strchr(str, ',');
+ strlcpy(outstr, chr ? chr + 1 : str, outlen);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_of_modalias);
+
static bool __acpi_match_device_cls(const struct acpi_device_id *id,
struct acpi_hardware_id *hwid)
{
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 4242c31..351b4a4 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -522,6 +522,7 @@ void acpi_bus_trim(struct acpi_device *start);
acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd);
int acpi_match_device_ids(struct acpi_device *device,
const struct acpi_device_id *ids);
+int acpi_of_modalias(struct acpi_device *adev, char *outstr, size_t outlen);
int acpi_create_dir(struct acpi_device *);
void acpi_remove_dir(struct acpi_device *);
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 0/3] Init device ids from ACPI of_compatible
From: Dan O'Donovan @ 2017-01-23 16:08 UTC (permalink / raw)
To: linux-acpi, Rafael J . Wysocki, Jarkko Nikula, Mika Westerberg,
Mark Brown
Cc: Len Brown, linux-i2c, Wolfram Sang, linux-spi, linux-kernel,
Dan O'Donovan
In-Reply-To: <cover.1468409668.git.leonard.crestez@intel.com>
(Re-submitting this patch set originally created by Leonard Crestez,
possibly abandoned by Leonard due to a change in employment)
When using devicetree, stuff like i2c_client.name or spi_device.modalias
is initialized to the first DT compatible id with the vendor prefix
stripped. Since some drivers rely on this in order to differentiate between
hardware variants try to replicate it when using ACPI with DT ids.
This also makes it so that the i2c_device_id parameter passed to probe is
non-NULL when matching with ACPI and DT ids.
Tested using ACPI overlays but there is no actual dependency. This series
just extends the PRP0001 feature to be more useful for I2C/SPI.
The patches only touches the ACPI-specific parts of the i2c and spi core.
Here is an example .dsl for an SPI accelerometer connected to minnowboard max:
Device (ACCL)
{
Name (_ADR, Zero)
Name (_HID, "PRP0001")
Name (_UID, One)
Method (_CRS, 0, Serialized)
{
Name (RBUF, ResourceTemplate ()
{
SPISerialBus(1, PolarityLow, FourWireMode, 16,
ControllerInitiated, 1000000, ClockPolarityLow,
ClockPhaseFirst, "\\_SB.SPI1",)
GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000,
"\\_SB.GPO2", 0x00, ResourceConsumer, , )
{ // Pin list
1
}
})
Return (RBUF)
}
Name (_DSD, Package ()
{
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () {"compatible", "st,lis3dh"},
}
})
}
Link to v2: https://lkml.org/lkml/2016/7/13/392
Changes:
* Use appropriate subject prefix for each subsystem (Mark Brown)
* Use ACPI info as before if getting OF info fails (Mark Brown)
* Minor cosmetic/readability improvements (Rafael J. Wysocki)
Link to v1: https://www.spinics.net/lists/linux-acpi/msg66469.html
Changes:
* Rebase on after acpi overlays got it.
* Change acpi_of_modalias outlen param to size_t
* Use {} after else
Crestez Dan Leonard (3):
ACPI / bus: Export acpi_of_modalias equiv of of_modalias_node
i2c: acpi: Initialize info.type from of_compatible
spi: acpi: Initialize modalias from of_compatible
drivers/acpi/bus.c | 35 +++++++++++++++++++++++++++++++++++
drivers/i2c/i2c-core.c | 8 +++++++-
drivers/spi/spi.c | 10 +++++++++-
include/acpi/acpi_bus.h | 1 +
4 files changed, 52 insertions(+), 2 deletions(-)
--
2.7.4
^ permalink raw reply
* Urgent Please,,
From: Joyes Dadi @ 2017-01-23 14:07 UTC (permalink / raw)
Good Day Dear,
My name is Ms. Joyes Dadi, I am glad you are reading this letter and I hope
we will start our communication and I know that this message will look strange,
surprising and probably unbelievable to you, but it is the reality. I want to
make a donation of money to you.
I contact you by the will of God. I am a firm German woman specialized in
mining gold and diamonds in Africa. But now, I'm very sick of a cancer. My
husband died in an accident two years ago with our two children and now I have
cancer of the esophagus that damaged almost all the cells in my system/agencies
and I'll die soon according to my doctor.
My most concern now is, we grew up in the orphanage and were married in
orphanage. If I die this deposited fund will soon be left alone in the hand of
the bank, and I do want to it that way. Please, if you can be reliable and
sincere to accept my humble proposal; I have (10.5Millions Euro) in a fixed
deposit account; I will order the Bank to transfer the money into your account
in your country immediately, and then you will take the fund to your country
and invest it to the orphanage homes Please, answer as quickly as possible.
God bless you.
Ms. Joyes Dadi
Email: joyesdadi767@citromail.hu
^ permalink raw reply
* Re: [PATCH v8 12/12] mux: support simplified bindings for single-user gpio mux
From: Peter Rosin @ 2017-01-23 10:24 UTC (permalink / raw)
To: Jonathan Cameron, linux-kernel
Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Jonathan Corbet,
Andrew Morton, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <33b29f7e-3e48-b85d-ccf9-93b1d67ac634@kernel.org>
On 2017-01-22 14:30, Jonathan Cameron wrote:
> On 18/01/17 15:57, Peter Rosin wrote:
>> Allow bindings for a GPIO controlled mux to be specified in the
>> mux consumer node.
>>
>> Signed-off-by: Peter Rosin <peda@axentia.se>
> Code is good as far as I am concerned. Only question is whether this
Hmmm, now that I think some more about it, the code supporting the
simplified binding (patch 12/12) is a bit fishy in one respect.
A driver that calls mux_control_get and gets a mux_control that happens
to be backed by an implicit mux chip (i.e. using the simplified binding)
will not be able to reverse the resource allocation with less than a
complete destruction of itself. Now, this is likely not a problem in
most cases, but I bet it will creep up at the most inopportune time. And
your remark that I'm the one that has to maintain this makes me dislike
this concept...
I.e. mux_control_put *should* reverse mux_control_get, but this simply
does not happen for the implicit mux chips, as implicit mux chips are
not put away until the owning device is put away.
Every time I have tried to come up with a way to implement the simplified
bindings I seem to hit one of these subtleties.
> is worth the hassle given the normal bindings don't give that high
> a burden in complexity!
I am missing an ack from Rob though.
> I don't really care either way:)
But Rob seems to care, this series just has to find a way to get out of
his too-much-churn-will-look-at-it-later list. I sadly don't know how to
pull that trick...
Cheers,
peda
^ permalink raw reply
* Re: [PATCH v5 5/5] i2c: mux: pca954x: Add irq-mask-enable to delay enabling irqs
From: Phil Reid @ 2017-01-23 9:02 UTC (permalink / raw)
To: Peter Rosin, wsa, robh+dt, mark.rutland, linux-i2c, devicetree
In-Reply-To: <71f756e1-4677-8b59-81a2-2b12fbf12621@axentia.se>
On 20/01/2017 06:56, Peter Rosin wrote:
> On 2017-01-19 08:48, Phil Reid wrote:
>> On 18/01/2017 20:19, Peter Rosin wrote:
>>> On 2017-01-17 09:00, Phil Reid wrote:
[snip]
>>> Hmm, this whole thing is fiddly and while it solves your problem it doesn't
>>> allow for solving the more general problem when there are "problematic"
>>> devices mixed with other devices. At least, I don't see it. And the
>>> limitations we are walking into with tracking number of enables etc suggests
>>> that we are attacking this at the wrong level. Maybe you should try to work
>>> around the hw limitations not in the pca954x driver, but in the irq core?
>>
>> I'm looking at the option of getting the hardware changed to not route
>> the irq for my chips thru the i2c mux. Fortunately the hardware is going thru a
>> revision for some other changes. Messing with the irq core sounds dangerous
>> with my level of knowledge.
>
> Yeah, but I bet you'd get some attention from people with more irq
> experience. That can't be bad :-)
>
>> The other way I think I can tackle it after reading the datasheet for the ltc1760 is that
>> it'll deassert it's irq (smbalert) line when the host sends a ARA request on the bus segment.
>> There's a driver in the kernel for this already, but it's not DT enable and doesn't
>> handle multiple bus segments. I'll have a look at that as well.
>> Pretty sure it would need the mux to become an irq parent as per patch 1-3 of this series.
>> This would be so the system can figure out which segment to do the poll on.
>
> Yeah sounds neater. It has the slight drawback that it may not work
> for pure i2c buses since it an SMB thing??
>
> BTW, why do you need special treatment for multiple segments? Will it not
> simply have an ARA appear on whatever i2c bus the device sits on? And if
> something requests to send an ARA message on a bus that happens to be a
> muxed segment, my mental picture is that the mux will be operated as usual
> so that the ARA appears on the muxed segment. Maybe I'm missing something?
My think was the following.
When the SMBALERT is asserted a ARA needs to be sent by the master.
If the device sending the SMBALERT is behind a mux when need to know which segment of the bus to enable.
Using shared interrupts should work I think, but you have to iterate thru each bus segment.
If the alert device is nested behind a couple of muxes this could get expensive.
But yeah otherwise I think the correct mux segment will get enabled automatically.
The current SMBALERT driver only seems to attached to the root i2c adapter.
>
>> But p4-5 could be dropped which is where we're stuck I think.
>
> Yes, I dislike to add a workaround for a specific case that might get
> in the way for anybody wishing to fix a bigger, more generic, problem...
>
>> Looking at this approach it shouldn't matter if the ltc1760 driver has registered yet or not.
>> This approach possibly has a lot more generic appeal I think.
>>
>> Thoughts on just submitting p1-3 for now while I figure out the SMB alert approach?
>
> Yes, looks like a plan. Thanks in advance!
Thanks, I'll do a new version with just p1-3.
>>>>
>>>> - if (!data->irq_mask)
>>>> - enable_irq(data->client->irq);
>>>> data->irq_mask |= BIT(pos);
>>>> + if (!data->irq_enabled
>>>> + && (data->irq_mask & mask_enable) == mask_enable) {
>>>
>>> I think the coding standard says that the && should be at the end of the
>>> first line. Didn't checkpatch complain?
>>
>> No it didn't complain. and I wasn't sure which way to do this.
>
> Ah, you need the --strict option for that to show up...
>
Haven't come across that option, I'll give it a try in future.
--
Regards
Phil Reid
^ permalink raw reply
* [PATCH v2 3/3] i2c: zx2967: add i2c controller driver for ZTE's zx2967 family
From: Baoyou Xie @ 2017-01-23 5:03 UTC (permalink / raw)
To: andy.shevchenko, jun.nie, wsa, robh+dt, mark.rutland
Cc: linux-arm-kernel, linux-i2c, devicetree, linux-kernel, shawnguo,
baoyou.xie, xie.baoyou, chen.chaokai, wang.qiang01
In-Reply-To: <1485147806-17792-1-git-send-email-baoyou.xie@linaro.org>
This patch adds i2c controller driver for ZTE's zx2967 family.
Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
---
drivers/i2c/busses/Kconfig | 9 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-zx2967.c | 690 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 700 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-zx2967.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e4a603e..9609f45 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1246,4 +1246,13 @@ config I2C_OPAL
This driver can also be built as a module. If so, the module will be
called as i2c-opal.
+config I2C_ZX2967
+ tristate "ZTE zx2967 I2C support"
+ depends on ARCH_ZX
+ default y
+ help
+ Selecting this option will add ZX2967 I2C driver.
+ This driver can also be built as a module. If so, the module will be
+ called zx2967_i2c.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index beb4809..16b2901 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
+obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c
new file mode 100644
index 0000000..d38b7b7
--- /dev/null
+++ b/drivers/i2c/busses/i2c-zx2967.c
@@ -0,0 +1,690 @@
+/*
+ * ZTE's zx2967 family i2c bus controller driver
+ *
+ * Copyright (C) 2017 ZTE Ltd.
+ *
+ * Author: Baoyou Xie <baoyou.xie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#define REG_CMD 0x04
+#define REG_DEVADDR_H 0x0C
+#define REG_DEVADDR_L 0x10
+#define REG_CLK_DIV_FS 0x14
+#define REG_CLK_DIV_HS 0x18
+#define REG_WRCONF 0x1C
+#define REG_RDCONF 0x20
+#define REG_DATA 0x24
+#define REG_STAT 0x28
+
+#define I2C_STOP 0
+#define I2C_MASTER BIT(0)
+#define I2C_ADDR_MODE_TEN BIT(1)
+#define I2C_IRQ_MSK_ENABLE BIT(3)
+#define I2C_RW_READ BIT(4)
+#define I2C_CMB_RW_EN BIT(5)
+#define I2C_START BIT(6)
+#define I2C_ADDR_MODE_TEN BIT(1)
+
+#define I2C_WFIFO_RESET BIT(7)
+#define I2C_RFIFO_RESET BIT(7)
+
+#define I2C_IRQ_ACK_CLEAR BIT(7)
+#define I2C_INT_MASK GENMASK(6, 0)
+
+#define I2C_TRANS_DONE BIT(0)
+#define I2C_ERROR_DEVICE BIT(1)
+#define I2C_ERROR_DATA BIT(2)
+#define I2C_ERROR_MASK GENMASK(2, 1)
+
+#define I2C_SR_BUSY BIT(6)
+
+#define I2C_SR_EDEVICE BIT(1)
+#define I2C_SR_EDATA BIT(2)
+
+#define I2C_FIFO_MAX 16
+
+#define I2C_TIMEOUT msecs_to_jiffies(1000)
+
+struct zx2967_i2c_info {
+ spinlock_t lock;
+ struct device *dev;
+ struct i2c_adapter adap;
+ struct clk *clk;
+ struct completion complete;
+ u32 clk_freq;
+ struct pinctrl *pin_ctrl;
+ void __iomem *reg_base;
+ size_t residue;
+ int id;
+ int irq;
+ int msg_rd;
+ u8 *buf;
+ u8 access_cnt;
+ bool is_suspended;
+};
+
+static void zx2967_i2c_writel(struct zx2967_i2c_info *zx_i2c,
+ u32 val, unsigned long reg)
+{
+ writel_relaxed(val, zx_i2c->reg_base + reg);
+}
+
+static u32 zx2967_i2c_readl(struct zx2967_i2c_info *zx_i2c, unsigned long reg)
+{
+ return readl_relaxed(zx_i2c->reg_base + reg);
+}
+
+static void zx2967_i2c_writesb(struct zx2967_i2c_info *zx_i2c,
+ void *data, unsigned long reg, int len)
+{
+ writesb(zx_i2c->reg_base + reg, data, len);
+}
+
+static void zx2967_i2c_readsb(struct zx2967_i2c_info *zx_i2c,
+ void *data, unsigned long reg, int len)
+{
+ readsb(zx_i2c->reg_base + reg, data, len);
+}
+
+static void zx2967_i2c_start_ctrl(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 status;
+ u32 ctl;
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT);
+ status |= I2C_IRQ_ACK_CLEAR;
+ zx2967_i2c_writel(zx_i2c, status, REG_STAT);
+
+ ctl = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ if (zx_i2c->msg_rd)
+ ctl |= I2C_RW_READ;
+ else
+ ctl &= ~I2C_RW_READ;
+ ctl &= ~I2C_CMB_RW_EN;
+ ctl |= I2C_START;
+ zx2967_i2c_writel(zx_i2c, ctl, REG_CMD);
+}
+
+static int zx2967_i2c_flush_fifos(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 val;
+ u32 offset;
+
+ if (zx_i2c->msg_rd) {
+ offset = REG_RDCONF;
+ val = I2C_RFIFO_RESET;
+ } else {
+ offset = REG_WRCONF;
+ val = I2C_WFIFO_RESET;
+ }
+
+ val |= zx2967_i2c_readl(zx_i2c, offset);
+ zx2967_i2c_writel(zx_i2c, val, offset);
+
+ return 0;
+}
+
+static int zx2967_i2c_empty_rx_fifo(struct zx2967_i2c_info *zx_i2c, u32 size)
+{
+ u8 val[I2C_FIFO_MAX] = {0};
+ int i;
+
+ if (size > I2C_FIFO_MAX) {
+ dev_err(zx_i2c->dev, "fifo size %d over the max value %d\n",
+ size, I2C_FIFO_MAX);
+ return -EINVAL;
+ }
+
+ zx2967_i2c_readsb(zx_i2c, val, REG_DATA, size);
+ for (i = 0; i < size; i++) {
+ *(zx_i2c->buf++) = val[i];
+ zx_i2c->residue--;
+ if (zx_i2c->residue <= 0)
+ break;
+ }
+
+ barrier();
+
+ return 0;
+}
+
+static int zx2967_i2c_fill_tx_fifo(struct zx2967_i2c_info *zx_i2c)
+{
+ u8 *buf = zx_i2c->buf;
+ size_t residue = zx_i2c->residue;
+
+ if (residue == 0) {
+ dev_err(zx_i2c->dev, "residue is %d\n", (int)residue);
+ return -EINVAL;
+ }
+
+ if (residue <= I2C_FIFO_MAX) {
+ zx2967_i2c_writesb(zx_i2c, buf, REG_DATA, residue);
+
+ /* Again update before writing to FIFO to make sure isr sees. */
+ zx_i2c->residue = 0;
+ zx_i2c->buf = NULL;
+ } else {
+ zx2967_i2c_writesb(zx_i2c, buf, REG_DATA, I2C_FIFO_MAX);
+ zx_i2c->residue -= I2C_FIFO_MAX;
+ zx_i2c->buf += I2C_FIFO_MAX;
+ }
+
+ barrier();
+
+ return 0;
+}
+
+static int zx2967_i2c_reset_hardware(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 val;
+ u32 clk_div;
+ u32 status;
+
+ val = I2C_MASTER | I2C_IRQ_MSK_ENABLE;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ clk_div = clk_get_rate(zx_i2c->clk) / zx_i2c->clk_freq - 1;
+ zx2967_i2c_writel(zx_i2c, clk_div, REG_CLK_DIV_FS);
+ zx2967_i2c_writel(zx_i2c, clk_div, REG_CLK_DIV_HS);
+
+ zx2967_i2c_writel(zx_i2c, I2C_FIFO_MAX - 1, REG_WRCONF);
+ zx2967_i2c_writel(zx_i2c, I2C_FIFO_MAX - 1, REG_RDCONF);
+ zx2967_i2c_writel(zx_i2c, 1, REG_RDCONF);
+
+ if (zx2967_i2c_flush_fifos(zx_i2c))
+ return -ETIMEDOUT;
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT);
+ if (status & (I2C_SR_BUSY))
+ return -EBUSY;
+ if (status & (I2C_SR_EDEVICE | I2C_SR_EDATA))
+ return -EIO;
+
+ enable_irq(zx_i2c->irq);
+
+ return 0;
+}
+
+static void zx2967_i2c_isr_clr(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 status;
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT);
+ status |= I2C_IRQ_ACK_CLEAR;
+ zx2967_i2c_writel(zx_i2c, status, REG_STAT);
+}
+
+static irqreturn_t zx2967_i2c_isr(int irq, void *dev_id)
+{
+ u32 status;
+ struct zx2967_i2c_info *zx_i2c = (struct zx2967_i2c_info *)dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&zx_i2c->lock, flags);
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT) & I2C_INT_MASK;
+ zx2967_i2c_isr_clr(zx_i2c);
+
+ if (status & I2C_ERROR_MASK) {
+ spin_unlock_irqrestore(&zx_i2c->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ if (status & I2C_TRANS_DONE)
+ complete(&zx_i2c->complete);
+
+ spin_unlock_irqrestore(&zx_i2c->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void zx2967_enable_tenbit(struct zx2967_i2c_info *zx_i2c, __u16 addr)
+{
+ u16 val = (addr >> 7) & 0x7;
+
+ if (val > 0) {
+ zx2967_i2c_writel(zx_i2c, val, REG_DEVADDR_H);
+ val = (zx2967_i2c_readl(zx_i2c, REG_CMD)) | I2C_ADDR_MODE_TEN;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+ }
+}
+
+static int
+zx2967_i2c_xfer_read_bytes(struct zx2967_i2c_info *zx_i2c, u32 bytes)
+{
+ unsigned long time_left;
+
+ reinit_completion(&zx_i2c->complete);
+ zx2967_i2c_writel(zx_i2c, bytes - 1, REG_RDCONF);
+ zx2967_i2c_start_ctrl(zx_i2c);
+
+ time_left = wait_for_completion_timeout(&zx_i2c->complete,
+ I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev, "read i2c transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ zx2967_i2c_empty_rx_fifo(zx_i2c, bytes);
+ return 0;
+}
+
+static int zx2967_i2c_xfer_read(struct zx2967_i2c_info *zx_i2c)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < zx_i2c->access_cnt; i++) {
+ ret = zx2967_i2c_xfer_read_bytes(zx_i2c, I2C_FIFO_MAX);
+ if (ret)
+ return ret;
+ }
+
+ if (zx_i2c->residue > 0) {
+ ret = zx2967_i2c_xfer_read_bytes(zx_i2c, I2C_FIFO_MAX);
+ if (ret)
+ return ret;
+ }
+
+ zx_i2c->residue = 0;
+ zx_i2c->access_cnt = 0;
+ return 0;
+}
+
+static int
+zx2967_i2c_xfer_write_bytes(struct zx2967_i2c_info *zx_i2c, u32 bytes)
+{
+ unsigned long time_left;
+
+ reinit_completion(&zx_i2c->complete);
+ zx2967_i2c_fill_tx_fifo(zx_i2c);
+ zx2967_i2c_start_ctrl(zx_i2c);
+
+ time_left = wait_for_completion_timeout(&zx_i2c->complete,
+ I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev, "write i2c transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int zx2967_i2c_xfer_write(struct zx2967_i2c_info *zx_i2c)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < zx_i2c->access_cnt; i++) {
+ ret = zx2967_i2c_xfer_write_bytes(zx_i2c, I2C_FIFO_MAX);
+ if (ret)
+ return ret;
+ }
+
+ if (zx_i2c->residue > 0) {
+ ret = zx2967_i2c_xfer_write_bytes(zx_i2c, I2C_FIFO_MAX);
+ if (ret)
+ return ret;
+ }
+
+ zx_i2c->residue = 0;
+ zx_i2c->access_cnt = 0;
+ return 0;
+}
+
+static int zx2967_i2c_xfer_msg(struct zx2967_i2c_info *zx_i2c,
+ struct i2c_msg *msg)
+{
+ if (msg->len == 0)
+ return -EINVAL;
+
+ zx2967_i2c_flush_fifos(zx_i2c);
+
+ zx_i2c->buf = msg->buf;
+ zx_i2c->residue = msg->len;
+ zx_i2c->access_cnt = msg->len / I2C_FIFO_MAX;
+ zx_i2c->msg_rd = (msg->flags & I2C_M_RD);
+
+ if (zx_i2c->msg_rd)
+ return zx2967_i2c_xfer_read(zx_i2c);
+
+ return zx2967_i2c_xfer_write(zx_i2c);
+}
+
+static int zx2967_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct zx2967_i2c_info *zx_i2c = i2c_get_adapdata(adap);
+ int ret;
+ int i;
+
+ if (zx_i2c->is_suspended)
+ return -EBUSY;
+
+ zx2967_i2c_writel(zx_i2c, (msgs->addr & 0x7f), REG_DEVADDR_L);
+ zx2967_i2c_writel(zx_i2c, (msgs->addr >> 7) & 0x7, REG_DEVADDR_H);
+ if (zx2967_i2c_readl(zx_i2c, REG_DEVADDR_H) > 0)
+ zx2967_enable_tenbit(zx_i2c, msgs->addr);
+
+ for (i = 0; i < num; i++) {
+ ret = zx2967_i2c_xfer_msg(zx_i2c, &msgs[i]);
+ if (num > 1)
+ usleep_range(1000, 2000);
+ if (ret)
+ return ret;
+ }
+
+ return num;
+}
+
+static void
+zx2967_smbus_xfer_prepare(struct zx2967_i2c_info *zx_i2c, u16 addr,
+ char read_write, u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ u32 val;
+
+ val = zx2967_i2c_readl(zx_i2c, REG_RDCONF);
+ val |= I2C_RFIFO_RESET;
+ zx2967_i2c_writel(zx_i2c, val, REG_RDCONF);
+ zx2967_i2c_writel(zx_i2c, (addr & 0x7f), REG_DEVADDR_L);
+
+ zx2967_enable_tenbit(zx_i2c, addr);
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val &= ~I2C_RW_READ;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ zx2967_i2c_writel(zx_i2c, command, REG_DATA);
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ zx2967_i2c_writel(zx_i2c, command, REG_DATA);
+ if (read_write == I2C_SMBUS_WRITE)
+ zx2967_i2c_writel(zx_i2c, data->byte, REG_DATA);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ zx2967_i2c_writel(zx_i2c, command, REG_DATA);
+ if (read_write == I2C_SMBUS_WRITE) {
+ zx2967_i2c_writel(zx_i2c, (data->word >> 8), REG_DATA);
+ zx2967_i2c_writel(zx_i2c, (data->word & 0xff),
+ REG_DATA);
+ }
+ break;
+ }
+}
+
+static int zx2967_smbus_xfer_read(struct zx2967_i2c_info *zx_i2c, int size,
+ union i2c_smbus_data *data)
+{
+ unsigned long time_left;
+ u8 buf[2];
+ u32 val;
+
+ reinit_completion(&zx_i2c->complete);
+
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val |= I2C_CMB_RW_EN;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val |= I2C_START;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ time_left = wait_for_completion_timeout(&zx_i2c->complete,
+ I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev, "i2c read transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ usleep_range(1000, 2000);
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ val = zx2967_i2c_readl(zx_i2c, REG_DATA);
+ data->byte = val;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ buf[0] = zx2967_i2c_readl(zx_i2c, REG_DATA);
+ buf[1] = zx2967_i2c_readl(zx_i2c, REG_DATA);
+ data->word = (buf[0] << 8) | buf[1];
+ break;
+ default:
+ dev_warn(zx_i2c->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int zx2967_smbus_xfer_write(struct zx2967_i2c_info *zx_i2c)
+{
+ unsigned long time_left;
+ u32 val;
+
+ reinit_completion(&zx_i2c->complete);
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val |= I2C_START;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ time_left = wait_for_completion_timeout(&zx_i2c->complete,
+ I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev, "i2c write transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int zx2967_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data)
+{
+ struct zx2967_i2c_info *zx_i2c = i2c_get_adapdata(adap);
+
+ if (size == I2C_SMBUS_QUICK)
+ read_write = I2C_SMBUS_WRITE;
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ case I2C_SMBUS_WORD_DATA:
+ zx2967_smbus_xfer_prepare(zx_i2c, addr, read_write,
+ command, size, data);
+ break;
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ if (read_write == I2C_SMBUS_READ)
+ return zx2967_smbus_xfer_read(zx_i2c, size, data);
+
+ return zx2967_smbus_xfer_write(zx_i2c);
+}
+
+#define ZX2967_I2C_FUNCS (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | \
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK)
+static u32 zx2967_i2c_func(struct i2c_adapter *adap)
+{
+ return ZX2967_I2C_FUNCS;
+}
+
+static int __maybe_unused zx2967_i2c_suspend(struct device *dev)
+{
+ struct zx2967_i2c_info *zx_i2c = dev_get_drvdata(dev);
+
+ zx_i2c->is_suspended = true;
+ if (__clk_get_enable_count(zx_i2c->clk) == 1)
+ clk_disable_unprepare(zx_i2c->clk);
+
+ return 0;
+}
+
+static int __maybe_unused zx2967_i2c_resume(struct device *dev)
+{
+ struct zx2967_i2c_info *zx_i2c = dev_get_drvdata(dev);
+
+ zx_i2c->is_suspended = false;
+ clk_prepare_enable(zx_i2c->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops zx2967_i2c_dev_pm_ops = {
+ .suspend = zx2967_i2c_suspend,
+ .resume = zx2967_i2c_resume,
+};
+#define ZX2967_I2C_DEV_PM_OPS (&zx2967_i2c_dev_pm_ops)
+#else
+#define ZX2967_I2C_DEV_PM_OPS NULL
+#endif
+
+static const struct i2c_algorithm zx2967_i2c_algo = {
+ .master_xfer = zx2967_i2c_xfer,
+ .smbus_xfer = zx2967_smbus_xfer,
+ .functionality = zx2967_i2c_func,
+};
+
+static const struct of_device_id zx2967_i2c_of_match[] = {
+ { .compatible = "zte,zx296718-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, zx2967_i2c_of_match);
+
+static int zx2967_i2c_probe(struct platform_device *pdev)
+{
+ struct zx2967_i2c_info *zx_i2c;
+ void __iomem *reg_base;
+ struct resource *res;
+ struct clk *clk;
+ int ret;
+
+ zx_i2c = devm_kzalloc(&pdev->dev, sizeof(*zx_i2c), GFP_KERNEL);
+ if (!zx_i2c)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg_base))
+ return PTR_ERR(reg_base);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "missing controller clock");
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable i2c_clk\n");
+ return ret;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ zx_i2c->irq = ret;
+
+ device_property_read_u32(&pdev->dev, "clock-frequency",
+ &zx_i2c->clk_freq);
+ zx_i2c->reg_base = reg_base;
+ zx_i2c->clk = clk;
+ zx_i2c->id = pdev->id;
+ zx_i2c->dev = &pdev->dev;
+
+ zx_i2c->pin_ctrl = devm_pinctrl_get_select(&pdev->dev, "pinctrl-0");
+ if (IS_ERR(zx_i2c->pin_ctrl))
+ dev_info(&pdev->dev, "pinctrl-0 pin is not specified in dts\n");
+
+ spin_lock_init(&zx_i2c->lock);
+ init_completion(&zx_i2c->complete);
+ platform_set_drvdata(pdev, zx_i2c);
+
+ ret = zx2967_i2c_reset_hardware(zx_i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize i2c controller\n");
+ goto err_clk_unprepare;
+ }
+
+ ret = devm_request_irq(&pdev->dev, zx_i2c->irq,
+ zx2967_i2c_isr, 0, dev_name(&pdev->dev), zx_i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %i\n", zx_i2c->irq);
+ goto err_clk_unprepare;
+ }
+
+ i2c_set_adapdata(&zx_i2c->adap, zx_i2c);
+ zx_i2c->adap.owner = THIS_MODULE;
+ zx_i2c->adap.class = I2C_CLASS_DEPRECATED;
+ strlcpy(zx_i2c->adap.name, "zx2967 i2c adapter",
+ sizeof(zx_i2c->adap.name));
+ zx_i2c->adap.algo = &zx2967_i2c_algo;
+ zx_i2c->adap.dev.parent = &pdev->dev;
+ zx_i2c->adap.nr = pdev->id;
+ zx_i2c->adap.dev.of_node = pdev->dev.of_node;
+
+ ret = i2c_add_numbered_adapter(&zx_i2c->adap);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add zx2967 i2c adapter\n");
+ goto err_clk_unprepare;
+ }
+
+ return 0;
+
+err_clk_unprepare:
+ clk_disable_unprepare(zx_i2c->clk);
+ return ret;
+}
+
+static int zx2967_i2c_remove(struct platform_device *pdev)
+{
+ struct zx2967_i2c_info *zx_i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&zx_i2c->adap);
+ clk_disable_unprepare(zx_i2c->clk);
+
+ return 0;
+}
+
+static struct platform_driver zx2967_i2c_driver = {
+ .probe = zx2967_i2c_probe,
+ .remove = zx2967_i2c_remove,
+ .driver = {
+ .name = "zx2967_i2c",
+ .of_match_table = zx2967_i2c_of_match,
+ .pm = ZX2967_I2C_DEV_PM_OPS,
+ },
+};
+module_platform_driver(zx2967_i2c_driver);
+
+MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
+MODULE_DESCRIPTION("ZTE zx2967 I2C Bus Controller driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH v2 2/3] MAINTAINERS: add zx2967 i2c controller driver to ARM ZTE architecture
From: Baoyou Xie @ 2017-01-23 5:03 UTC (permalink / raw)
To: andy.shevchenko, jun.nie, wsa, robh+dt, mark.rutland
Cc: linux-arm-kernel, linux-i2c, devicetree, linux-kernel, shawnguo,
baoyou.xie, xie.baoyou, chen.chaokai, wang.qiang01
In-Reply-To: <1485147806-17792-1-git-send-email-baoyou.xie@linaro.org>
Add the zx2967 i2c controller driver as maintained by ARM ZTE
architecture maintainers, as they're parts of the core IP.
Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 275c434..757c098 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1987,12 +1987,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-zx/
F: drivers/clk/zte/
+F: drivers/i2c/busses/i2c-zx2967.c
F: drivers/reset/reset-zx2967.c
F: drivers/soc/zte/
F: drivers/thermal/zx*
F: drivers/watchdog/zx2967_wdt.c
F: Documentation/devicetree/bindings/arm/zte.txt
F: Documentation/devicetree/bindings/clock/zx296702-clk.txt
+F: Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
F: Documentation/devicetree/bindings/reset/zte,zx2967-reset.txt
F: Documentation/devicetree/bindings/soc/zte/
F: Documentation/devicetree/bindings/thermal/zx*
--
2.7.4
^ permalink raw reply related
* [PATCH v2 1/3] dt: bindings: add documentation for zx2967 family i2c controller
From: Baoyou Xie @ 2017-01-23 5:03 UTC (permalink / raw)
To: andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w,
jun.nie-QSEj5FYQhm4dnm+yROfE0A, wsa-z923LK4zBo2bacvFa/9K2g,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
baoyou.xie-QSEj5FYQhm4dnm+yROfE0A,
xie.baoyou-Th6q7B73Y6EnDS1+zs4M5A,
chen.chaokai-Th6q7B73Y6EnDS1+zs4M5A,
wang.qiang01-Th6q7B73Y6EnDS1+zs4M5A
This patch adds dt-binding documentation for zx2967 family
i2c controller.
Signed-off-by: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
.../devicetree/bindings/i2c/i2c-zx2967.txt | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt b/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
new file mode 100644
index 0000000..cb806d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
@@ -0,0 +1,22 @@
+ZTE zx2967 I2C controller
+
+Required properties:
+ - compatible: must be "zte,zx296718-i2c"
+ - reg: physical address and length of the device registers
+ - interrupts: a single interrupt specifier
+ - clocks: clock for the device
+ - #address-cells: should be <1>
+ - #size-cells: should be <0>
+ - clock-frequency: the desired I2C bus clock frequency.
+
+Examples:
+
+ i2c@112000 {
+ compatible = "zte,zx296718-i2c";
+ reg = <0x00112000 0x1000>;
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&osc24m>;
+ #address-cells = <1>
+ #size-cells = <0>;
+ clock-frequency = <1600000>;
+ };
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v1 3/3] i2c: zx2967: add i2c controller driver for ZTE's zx2967 family
From: Shawn Guo @ 2017-01-23 2:50 UTC (permalink / raw)
To: Baoyou Xie
Cc: jun.nie, wsa, robh+dt, mark.rutland, linux-arm-kernel, linux-i2c,
devicetree, linux-kernel, xie.baoyou, chen.chaokai, wang.qiang01
In-Reply-To: <1484901827-16369-3-git-send-email-baoyou.xie@linaro.org>
On Fri, Jan 20, 2017 at 04:43:47PM +0800, Baoyou Xie wrote:
> +static int zx2967_i2c_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct zx2967_i2c_info *zx_i2c = NULL;
> + struct clk *clk;
> + void __iomem *reg_base;
> + int ret = 0;
> +
> + zx_i2c = devm_kzalloc(&pdev->dev, sizeof(*zx_i2c), GFP_KERNEL);
> + if (!zx_i2c)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "missing io resource\n");
> + return -EINVAL;
> + }
> +
> + reg_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(reg_base))
> + return PTR_ERR(reg_base);
> +
> + clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(clk)) {
> + dev_err(&pdev->dev, "missing controller clock");
> + return PTR_ERR(clk);
> + }
> +
> + ret = clk_prepare_enable(clk);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to enable i2c_clk\n");
> + return ret;
> + }
> +
> + of_property_read_u32(pdev->dev.of_node,
> + "clock-frequency", &zx_i2c->clk_freq);
> + zx_i2c->reg_base = reg_base;
> + zx_i2c->clk = clk;
> + zx_i2c->irq = platform_get_irq(pdev, 0);
> + zx_i2c->id = pdev->id;
> + zx_i2c->dev = &pdev->dev;
> +
> + zx_i2c->pin_scl = devm_pinctrl_get_select(&pdev->dev, "scl");
> + if (IS_ERR(zx_i2c->pin_scl))
> + dev_info(&pdev->dev, "scl pin is not specified in dts\n");
> +
> + zx_i2c->pin_sda = devm_pinctrl_get_select(&pdev->dev, "sda");
> + if (IS_ERR(zx_i2c->pin_sda))
> + dev_info(&pdev->dev, "sda pin is not specified in dts\n");
I don't understand why this is necessary. For most cases, we only need
to specify needed pins in property 'pinctrl-0' and 'default' in
'pinctrl-names', and pinctrl/driver core will just set up the pins for
the device.
Shawn
^ permalink raw reply
* Re: [PATCH v3 3/3] iio: distance: srf08: add driver ABI documentation
From: Jonathan Cameron @ 2017-01-22 22:11 UTC (permalink / raw)
To: Andreas Klinger
Cc: knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
pmeerw-jW+XmwGofnusTnJN9+BGXg, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, ktsai-GubuWUlQtMwciDkP5Hr2oA,
wsa-z923LK4zBo2bacvFa/9K2g, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, trivial-DgEjT+Ai2ygdnm+yROfE0A,
mranostay-Re5JQEeQqe8AvxtiuMwx3w,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170122204110.GA1840@andreas>
On 22/01/17 20:41, Andreas Klinger wrote:
> Hi Jonathan,
>
> see question below.
>
> Andreas
>
>
> Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> schrieb am Sun, 22. Jan 13:41:
>> On 17/01/17 13:50, Andreas Klinger wrote:
>>> Add sysfs-bus-iio-distance-srf08 for individual attributes of the driver,
>>> especially:
>>> - sensitivity which the device documentation calls gain for amplifying the
>>> signal
>>> - max_range for limiting the maximum distance for expected echos and
>>> therefore limiting the time waiting for telegrams
>>>
>>> Signed-off-by: Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>
>>> ---
>>> .../ABI/testing/sysfs-bus-iio-distance-srf08 | 27 ++++++++++++++++++++++
>>> 1 file changed, 27 insertions(+)
>>> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
>>> new file mode 100644
>>> index 000000000000..e96c28064748
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
>>> @@ -0,0 +1,27 @@
>>> +What /sys/bus/iio/devices/iio:deviceX/in_distance_raw
>>> +Date: January 2017
>>> +KernelVersion: 4.11
>>> +Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> +Description:
>>> + Get the current distance in meters between the sensor and
>>> + the first object recognized
>> Generally no need to document what are effectively standard elements.
>> Just leads to the possibility of disagreements in the docs!
>> Still no harm here really.
>>> +
>>> +What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
>>> +Date: January 2017
>>> +KernelVersion: 4.11
>>> +Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> +Description:
>>> + Show or set the gain boost of the amp, from 0-31 range.
>>> + default 31
>>> +
>>> +What /sys/bus/iio/devices/iio:deviceX/sensor_max_range
>>> +Date: January 2017
>>> +KernelVersion: 4.11
>>> +Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> +Description:
>>> + Show or set the maximum range between the sensor and the
>>> + first object echoed in millimeters.
>>> + This setting limits the time the driver is waiting for a
>>> + echo.
>>> + Can be set between 43 and 11008 in a grid of 43 mm.
>>> + default 6020
>> This needs to be in the same units as the range - so m.
>
> Just to clarify:
> There should be a floating point number be passed to the driver and being parsed
> there, e. g.:
> 0.043 or 6.020
> right?
That's fine. Just use the core functions that are already in place for this in
your own parsing. iio_str_to_fixpoint should do the job for you.
It may seem fiddly but however carefully we pick a base unit we never allow enough
range to represent everything that turns up in the future. Hence the trickery with
'fixed point' values where we can redefine the two parts.
>
> Or should this number be passed using the scale attribute like the raw value
> does? So that the scale is used by both the value itself and the attribute.
Interesting point. I'm trying to think of an equivalent where the unit is shared like
this. The event attributes (thresholds) do use scale, but they aren't really
the same thing...
I think if we wanted to do that we'd need to call it something that made it clear.
More than possible that we'll get a colour sensor down the line with a max range or
something silly like that (focusing optics perhaps?) - so may not always be obvious.
Could stick a _raw on the end of it.
Anyone else reading this and have any thoughts?
Jonathan
>
>> I'm not 100% sure this is the best ABI we can do for this. However, supporting
>> this as a legacy abi if we come up with something more general will be a very
>> small burden, so lets not stall the driver on this!
>>
>> Jonathan
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH v3 3/3] iio: distance: srf08: add driver ABI documentation
From: Andreas Klinger @ 2017-01-22 20:41 UTC (permalink / raw)
To: Jonathan Cameron
Cc: knaack.h, lars, pmeerw, linux-iio, linux-kernel, ktsai, wsa,
robh+dt, pawel.moll, mark.rutland, ijc+devicetree, galak, trivial,
mranostay, linux-i2c, devicetree
In-Reply-To: <2e87aeed-a611-e160-c245-f5b16f2ef304@kernel.org>
Hi Jonathan,
see question below.
Andreas
Jonathan Cameron <jic23@kernel.org> schrieb am Sun, 22. Jan 13:41:
> On 17/01/17 13:50, Andreas Klinger wrote:
> > Add sysfs-bus-iio-distance-srf08 for individual attributes of the driver,
> > especially:
> > - sensitivity which the device documentation calls gain for amplifying the
> > signal
> > - max_range for limiting the maximum distance for expected echos and
> > therefore limiting the time waiting for telegrams
> >
> > Signed-off-by: Andreas Klinger <ak@it-klinger.de>
> > ---
> > .../ABI/testing/sysfs-bus-iio-distance-srf08 | 27 ++++++++++++++++++++++
> > 1 file changed, 27 insertions(+)
> > create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> > new file mode 100644
> > index 000000000000..e96c28064748
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> > @@ -0,0 +1,27 @@
> > +What /sys/bus/iio/devices/iio:deviceX/in_distance_raw
> > +Date: January 2017
> > +KernelVersion: 4.11
> > +Contact: linux-iio@vger.kernel.org
> > +Description:
> > + Get the current distance in meters between the sensor and
> > + the first object recognized
> Generally no need to document what are effectively standard elements.
> Just leads to the possibility of disagreements in the docs!
> Still no harm here really.
> > +
> > +What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
> > +Date: January 2017
> > +KernelVersion: 4.11
> > +Contact: linux-iio@vger.kernel.org
> > +Description:
> > + Show or set the gain boost of the amp, from 0-31 range.
> > + default 31
> > +
> > +What /sys/bus/iio/devices/iio:deviceX/sensor_max_range
> > +Date: January 2017
> > +KernelVersion: 4.11
> > +Contact: linux-iio@vger.kernel.org
> > +Description:
> > + Show or set the maximum range between the sensor and the
> > + first object echoed in millimeters.
> > + This setting limits the time the driver is waiting for a
> > + echo.
> > + Can be set between 43 and 11008 in a grid of 43 mm.
> > + default 6020
> This needs to be in the same units as the range - so m.
Just to clarify:
There should be a floating point number be passed to the driver and being parsed
there, e. g.:
0.043 or 6.020
right?
Or should this number be passed using the scale attribute like the raw value
does? So that the scale is used by both the value itself and the attribute.
> I'm not 100% sure this is the best ABI we can do for this. However, supporting
> this as a legacy abi if we come up with something more general will be a very
> small burden, so lets not stall the driver on this!
>
> Jonathan
> >
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
^ permalink raw reply
* Re: [PATCH v3 2/3] iio: distance: srf08: add IIO driver for us ranger
From: Jonathan Cameron @ 2017-01-22 13:46 UTC (permalink / raw)
To: Andreas Klinger, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, ktsai-GubuWUlQtMwciDkP5Hr2oA,
wsa-z923LK4zBo2bacvFa/9K2g, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, trivial-DgEjT+Ai2ygdnm+yROfE0A,
mranostay-Re5JQEeQqe8AvxtiuMwx3w,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170117134938.GA22892@andreas>
On 17/01/17 13:49, Andreas Klinger wrote:
> This is the IIO driver for devantech srf08 ultrasonic ranger which can be
> used to measure the distances to an object.
>
> The sensor supports I2C with some registers.
>
> Supported Features include:
> - read the distance in ranging mode in centimeters
> - output of the driver is directly the read value
> - together with the scale the driver delivers the distance in meters
> - only the first echo of the nearest object is delivered
> - set sensitivity as analog value in the range of 0-31 means setting
> gain register on device
> - set range registers; userspace enters max. range in millimeters in
> 43 mm steps
>
> Features not supported by this driver:
> - ranging mode in inches or in microseconds
> - ANN mode
> - change I2C address through this driver
> - light sensor
>
> The driver was added in the directory "proximity" of the iio subsystem and
> the menu in den config is now called "Proximity and distance sensors"
>
> Signed-off-by: Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>
One suggestion inline. Otherwise, just the units of your new max_range
attribute as per comment on ABI docs.
Looking good.
Jonathan
> ---
> drivers/iio/proximity/Kconfig | 13 +-
> drivers/iio/proximity/Makefile | 1 +
> drivers/iio/proximity/srf08.c | 405 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 418 insertions(+), 1 deletion(-)
> create mode 100644 drivers/iio/proximity/srf08.c
>
> diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
> index ef4c73db5b53..ab96cb7a0054 100644
> --- a/drivers/iio/proximity/Kconfig
> +++ b/drivers/iio/proximity/Kconfig
> @@ -18,7 +18,7 @@ config AS3935
>
> endmenu
>
> -menu "Proximity sensors"
> +menu "Proximity and distance sensors"
>
> config LIDAR_LITE_V2
> tristate "PulsedLight LIDAR sensor"
> @@ -45,4 +45,15 @@ config SX9500
> To compile this driver as a module, choose M here: the
> module will be called sx9500.
>
> +config SRF08
> + tristate "Devantech SRF08 ultrasonic ranger sensor"
> + depends on I2C
> + help
> + Say Y here to build a driver for Devantech SRF08 ultrasonic
> + ranger sensor. This driver can be used to measure the distance
> + of objects.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called srf08.
> +
> endmenu
> diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
> index 9aadd9a8ee99..e914c2a5dd49 100644
> --- a/drivers/iio/proximity/Makefile
> +++ b/drivers/iio/proximity/Makefile
> @@ -5,4 +5,5 @@
> # When adding new entries keep the list in alphabetical order
> obj-$(CONFIG_AS3935) += as3935.o
> obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
> +obj-$(CONFIG_SRF08) += srf08.o
> obj-$(CONFIG_SX9500) += sx9500.o
> diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c
> new file mode 100644
> index 000000000000..faab6cf3f1a2
> --- /dev/null
> +++ b/drivers/iio/proximity/srf08.c
> @@ -0,0 +1,405 @@
> +/*
> + * srf08.c - Support for Devantech SRF08 ultrasonic ranger
> + *
> + * Copyright (c) 2016 Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License. See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * For details about the device see:
> + * http://www.robot-electronics.co.uk/htm/srf08tech.html
> + */
> +
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/bitops.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +/* registers of SRF08 device */
> +#define SRF08_WRITE_COMMAND 0x00 /* Command Register */
> +#define SRF08_WRITE_MAX_GAIN 0x01 /* Max Gain Register: 0 .. 31 */
> +#define SRF08_WRITE_RANGE 0x02 /* Range Register: 0 .. 255 */
> +#define SRF08_READ_SW_REVISION 0x00 /* Software Revision */
> +#define SRF08_READ_LIGHT 0x01 /* Light Sensor during last echo */
> +#define SRF08_READ_ECHO_1_HIGH 0x02 /* Range of first echo received */
> +#define SRF08_READ_ECHO_1_LOW 0x03 /* Range of first echo received */
> +
> +#define SRF08_CMD_RANGING_CM 0x51 /* Ranging Mode - Result in cm */
> +
> +#define SRF08_DEFAULT_GAIN 1025 /* default analogue value of Gain */
> +#define SRF08_DEFAULT_RANGE 6020 /* default value of Range in mm */
> +
> +struct srf08_data {
> + struct i2c_client *client;
> + int sensitivity; /* Gain */
> + int range_mm; /* max. Range in mm */
> + struct mutex lock;
> +};
> +
> +/*
> + * in the documentation one can read about the "Gain" of the device
> + * which is used here for amplifying the signal and filtering out unwanted
> + * ones.
> + * But with ADC's this term is already used differently and that's why it
> + * is called "Sensitivity" here.
> + */
> +static const int srf08_sensitivity[] = {
> + 94, 97, 100, 103, 107, 110, 114, 118,
> + 123, 128, 133, 139, 145, 152, 159, 168,
> + 177, 187, 199, 212, 227, 245, 265, 288,
> + 317, 352, 395, 450, 524, 626, 777, 1025 };
> +
> +static int srf08_read_ranging(struct srf08_data *data)
> +{
> + struct i2c_client *client = data->client;
> + int ret, i;
> + int waittime;
> +
> + mutex_lock(&data->lock);
> +
> + ret = i2c_smbus_write_byte_data(data->client,
> + SRF08_WRITE_COMMAND, SRF08_CMD_RANGING_CM);
> + if (ret < 0) {
> + dev_err(&client->dev, "write command - err: %d\n", ret);
> + mutex_unlock(&data->lock);
> + return ret;
> + }
> +
> + /*
> + * we read here until a correct version number shows up as
> + * suggested by the documentation
> + *
> + * with an ultrasonic speed of 343 m/s and a roundtrip of it
> + * sleep the expected duration and try to read from the device
> + * if nothing useful is read try it in a shorter grid
> + *
> + * polling for not more than 20 ms should be enough
> + */
> + waittime = 1 + data->range_mm / 172;
> + msleep(waittime);
> + for (i = 0; i < 4; i++) {
> + ret = i2c_smbus_read_byte_data(data->client,
> + SRF08_READ_SW_REVISION);
> +
> + /* check if a valid version number is read */
> + if (ret < 255 && ret > 0)
> + break;
> + msleep(5);
> + }
> +
> + if (ret >= 255 || ret <= 0) {
> + dev_err(&client->dev, "device not ready\n");
> + mutex_unlock(&data->lock);
> + return -EIO;
> + }
> +
> + ret = i2c_smbus_read_word_swapped(data->client,
> + SRF08_READ_ECHO_1_HIGH);
> + if (ret < 0) {
> + dev_err(&client->dev, "cannot read distance: ret=%d\n", ret);
> + mutex_unlock(&data->lock);
> + return ret;
> + }
> +
> + mutex_unlock(&data->lock);
> +
> + return ret;
> +}
> +
> +static int srf08_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *channel, int *val,
> + int *val2, long mask)
> +{
> + struct srf08_data *data = iio_priv(indio_dev);
> + int ret;
> +
> + if (channel->type != IIO_DISTANCE)
> + return -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + ret = srf08_read_ranging(data);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + /* 1 LSB is 1 cm */
> + *val = 0;
> + *val2 = 10000;
> + return IIO_VAL_INT_PLUS_MICRO;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static ssize_t srf08_show_range_mm_available(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int i, len = 0;
> +
> + for (i = 0; i < 256; i++)
> + len += scnprintf(buf + len, PAGE_SIZE - len,
> + "%d ", (i + 1) * 43);
Does this fit in the page length? Guess it probably does. See comment below.
> +
> + buf[len - 1] = '\n';
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_max_range_available, S_IRUGO,
> + srf08_show_range_mm_available, NULL, 0);
> +
> +static ssize_t srf08_show_range_mm(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct srf08_data *data = iio_priv(indio_dev);
> +
> + return sprintf(buf, "%d\n", data->range_mm);
> +}
> +
> +/*
> + * set the range of the sensor to an even multiple of 43 mm
> + * which corresponds to 1 LSB in the register
> + *
> + * register value corresponding range
> + * 0x00 43 mm
> + * 0x01 86 mm
> + * 0x02 129 mm
> + * ...
> + * 0xFF 11008 mm
> + */
> +static ssize_t srf08_write_range_mm(struct srf08_data *data, unsigned int val)
> +{
> + int ret;
> + struct i2c_client *client = data->client;
> + unsigned int mod;
> + u8 regval;
> +
> + ret = val / 43 - 1;
> + mod = val % 43;
> +
> + if (mod || (ret < 0) || (ret > 255))
> + return -EINVAL;
> +
> + regval = ret;
> +
> + mutex_lock(&data->lock);
> +
> + ret = i2c_smbus_write_byte_data(client, SRF08_WRITE_RANGE, regval);
> + if (ret < 0) {
> + dev_err(&client->dev, "write_range - err: %d\n", ret);
> + mutex_unlock(&data->lock);
> + return ret;
> + }
> +
> + data->range_mm = val;
> +
> + mutex_unlock(&data->lock);
> +
> + return 0;
> +}
> +
> +static ssize_t srf08_store_range_mm(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct srf08_data *data = iio_priv(indio_dev);
> + int ret;
> + unsigned int val;
> +
> + ret = kstrtouint(buf, 10, &val);
> + if (ret)
> + return ret;
> +
> + ret = srf08_write_range_mm(data, val);
> + if (ret < 0)
> + return ret;
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_max_range, S_IRUGO | S_IWUSR,
> + srf08_show_range_mm, srf08_store_range_mm, 0);
> +
> +static ssize_t srf08_show_sensitivity_available(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int i, len = 0;
> +
> + for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++)
> + len += sprintf(buf + len, "%d ", srf08_sensitivity[i]);
> +
> + len += sprintf(buf + len, "\n");
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_sensitivity_available, S_IRUGO,
> + srf08_show_sensitivity_available, NULL, 0);
> +
> +static ssize_t srf08_show_sensitivity(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct srf08_data *data = iio_priv(indio_dev);
> + int len;
> +
> + len = sprintf(buf, "%d\n", data->sensitivity);
> +
> + return len;
> +}
> +
> +static ssize_t srf08_write_sensitivity(struct srf08_data *data,
> + unsigned int val)
> +{
> + struct i2c_client *client = data->client;
> + int ret, i;
> + u8 regval;
> +
> + for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++)
> + if (val == srf08_sensitivity[i]) {
> + regval = i;
> + break;
> + }
> +
> + if (i >= ARRAY_SIZE(srf08_sensitivity))
> + return -EINVAL;
> +
> + mutex_lock(&data->lock);
> +
> + ret = i2c_smbus_write_byte_data(client,
> + SRF08_WRITE_MAX_GAIN, regval);
> + if (ret < 0) {
> + dev_err(&client->dev, "write_sensitivity - err: %d\n", ret);
> + mutex_unlock(&data->lock);
> + return ret;
> + }
> +
> + data->sensitivity = val;
> +
> + mutex_unlock(&data->lock);
> +
> + return 0;
> +}
> +
> +static ssize_t srf08_store_sensitivity(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct srf08_data *data = iio_priv(indio_dev);
> + int ret;
> + unsigned int val;
> +
> + ret = kstrtouint(buf, 10, &val);
> + if (ret)
> + return ret;
> +
> + ret = srf08_write_sensitivity(data, val);
> + if (ret < 0)
> + return ret;
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR,
> + srf08_show_sensitivity, srf08_store_sensitivity, 0);
> +
> +static struct attribute *srf08_attributes[] = {
> + &iio_dev_attr_sensor_max_range.dev_attr.attr,
> + &iio_dev_attr_sensor_max_range_available.dev_attr.attr,
Might be useful to note we now allow more complex available descriptions
though they are not well documented at the moment (my fault - had a busy
few weeks). See Documentation/ABI/testing/sysfs-bus-iio-dac-dpot for
a simplex example.
> + &iio_dev_attr_sensor_sensitivity.dev_attr.attr,
> + &iio_dev_attr_sensor_sensitivity_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group srf08_attribute_group = {
> + .attrs = srf08_attributes,
> +};
> +
> +static const struct iio_chan_spec srf08_channels[] = {
> + {
> + .type = IIO_DISTANCE,
> + .info_mask_separate =
> + BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE),
> + },
> +};
> +
> +static const struct iio_info srf08_info = {
> + .read_raw = srf08_read_raw,
> + .attrs = &srf08_attribute_group,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int srf08_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct iio_dev *indio_dev;
> + struct srf08_data *data;
> + int ret;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_READ_BYTE_DATA |
> + I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
> + I2C_FUNC_SMBUS_READ_WORD_DATA))
> + return -ENODEV;
> +
> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_dev);
> + i2c_set_clientdata(client, indio_dev);
> + data->client = client;
> +
> + indio_dev->name = "srf08";
> + indio_dev->dev.parent = &client->dev;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->info = &srf08_info;
> + indio_dev->channels = srf08_channels;
> + indio_dev->num_channels = ARRAY_SIZE(srf08_channels);
> +
> + mutex_init(&data->lock);
> +
> + /*
> + * set default values of device here
> + * these register values cannot be read from the hardware
> + * therefore set driver specific default values
> + */
> + ret = srf08_write_sensitivity(data, SRF08_DEFAULT_GAIN);
> + if (ret < 0)
> + return ret;
> +
> + ret = srf08_write_range_mm(data, SRF08_DEFAULT_RANGE);
> + if (ret < 0)
> + return ret;
> +
> + return devm_iio_device_register(&client->dev, indio_dev);
> +}
> +
> +static const struct i2c_device_id srf08_id[] = {
> + { "srf08", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, srf08_id);
> +
> +static struct i2c_driver srf08_driver = {
> + .driver = {
> + .name = "srf08",
> + },
> + .probe = srf08_probe,
> + .id_table = srf08_id,
> +};
> +module_i2c_driver(srf08_driver);
> +
> +MODULE_AUTHOR("Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>");
> +MODULE_DESCRIPTION("Devantech SRF08 ultrasonic ranger driver");
> +MODULE_LICENSE("GPL");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 3/3] iio: distance: srf08: add driver ABI documentation
From: Jonathan Cameron @ 2017-01-22 13:41 UTC (permalink / raw)
To: Andreas Klinger, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, ktsai-GubuWUlQtMwciDkP5Hr2oA,
wsa-z923LK4zBo2bacvFa/9K2g, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, trivial-DgEjT+Ai2ygdnm+yROfE0A,
mranostay-Re5JQEeQqe8AvxtiuMwx3w,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170117135006.GA22903@andreas>
On 17/01/17 13:50, Andreas Klinger wrote:
> Add sysfs-bus-iio-distance-srf08 for individual attributes of the driver,
> especially:
> - sensitivity which the device documentation calls gain for amplifying the
> signal
> - max_range for limiting the maximum distance for expected echos and
> therefore limiting the time waiting for telegrams
>
> Signed-off-by: Andreas Klinger <ak-n176/SwNRljddJNmlsFzeA@public.gmane.org>
> ---
> .../ABI/testing/sysfs-bus-iio-distance-srf08 | 27 ++++++++++++++++++++++
> 1 file changed, 27 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> new file mode 100644
> index 000000000000..e96c28064748
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
> @@ -0,0 +1,27 @@
> +What /sys/bus/iio/devices/iio:deviceX/in_distance_raw
> +Date: January 2017
> +KernelVersion: 4.11
> +Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +Description:
> + Get the current distance in meters between the sensor and
> + the first object recognized
Generally no need to document what are effectively standard elements.
Just leads to the possibility of disagreements in the docs!
Still no harm here really.
> +
> +What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
> +Date: January 2017
> +KernelVersion: 4.11
> +Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +Description:
> + Show or set the gain boost of the amp, from 0-31 range.
> + default 31
> +
> +What /sys/bus/iio/devices/iio:deviceX/sensor_max_range
> +Date: January 2017
> +KernelVersion: 4.11
> +Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +Description:
> + Show or set the maximum range between the sensor and the
> + first object echoed in millimeters.
> + This setting limits the time the driver is waiting for a
> + echo.
> + Can be set between 43 and 11008 in a grid of 43 mm.
> + default 6020
This needs to be in the same units as the range - so m.
I'm not 100% sure this is the best ABI we can do for this. However, supporting
this as a legacy abi if we come up with something more general will be a very
small burden, so lets not stall the driver on this!
Jonathan
>
^ permalink raw reply
* Re: [PATCH v8 12/12] mux: support simplified bindings for single-user gpio mux
From: Jonathan Cameron @ 2017-01-22 13:30 UTC (permalink / raw)
To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Jonathan Corbet,
Andrew Morton, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1484755035-25927-13-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 18/01/17 15:57, Peter Rosin wrote:
> Allow bindings for a GPIO controlled mux to be specified in the
> mux consumer node.
>
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Code is good as far as I am concerned. Only question is whether this
is worth the hassle given the normal bindings don't give that high
a burden in complexity!
I don't really care either way:)
> ---
> drivers/mux/Kconfig | 5 +----
> drivers/mux/mux-core.c | 23 +++++++++++++++++++++--
> drivers/mux/mux-gpio.c | 28 +++++++++++++++++++++-------
> drivers/mux/mux-gpio.h | 13 +++++++++++++
> 4 files changed, 56 insertions(+), 13 deletions(-)
> create mode 100644 drivers/mux/mux-gpio.h
>
> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> index d13fcf98958e..cd161c228537 100644
> --- a/drivers/mux/Kconfig
> +++ b/drivers/mux/Kconfig
> @@ -29,7 +29,7 @@ config MUX_ADG792A
> be called mux-adg792a.
>
> config MUX_GPIO
> - tristate "GPIO-controlled Multiplexer"
> + bool "GPIO-controlled Multiplexer"
> depends on OF && GPIOLIB
> help
> GPIO-controlled Multiplexer controller.
> @@ -39,7 +39,4 @@ config MUX_GPIO
> states. The GPIO pins can be connected (by the hardware) to several
> multiplexers, which in that case will be operated in parallel.
>
> - To compile the driver as a module, choose M here: the module will
> - be called mux-gpio.
> -
> endif
> diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c
> index 16a61253d164..0caafd6f5a77 100644
> --- a/drivers/mux/mux-core.c
> +++ b/drivers/mux/mux-core.c
> @@ -21,6 +21,8 @@
> #include <linux/of_platform.h>
> #include <linux/slab.h>
>
> +#include "mux-gpio.h"
> +
> static struct class mux_class = {
> .name = "mux",
> .owner = THIS_MODULE,
> @@ -314,9 +316,26 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
> ret = of_parse_phandle_with_args(np,
> "mux-controls", "#mux-control-cells",
> index, &args);
> +
> +#ifdef CONFIG_MUX_GPIO
> + if (ret == -ENOENT && !mux_name) {
> + mux_chip = mux_gpio_alloc(dev);
> + if (!IS_ERR(mux_chip)) {
> + ret = devm_mux_chip_register(dev, mux_chip);
> + if (ret < 0)
> + return ERR_PTR(ret);
> + get_device(&mux_chip->dev);
> + return mux_chip->mux;
> + }
> +
> + ret = PTR_ERR(mux_chip);
> + }
> +#endif
> +
> if (ret) {
> - dev_err(dev, "%s: failed to get mux-control %s(%i)\n",
> - np->full_name, mux_name ?: "", index);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "%s: failed to get mux-control %s(%i)\n",
> + np->full_name, mux_name ?: "", index);
> return ERR_PTR(ret);
> }
>
> diff --git a/drivers/mux/mux-gpio.c b/drivers/mux/mux-gpio.c
> index 48ca4d6b4fc7..2ab8735e3415 100644
> --- a/drivers/mux/mux-gpio.c
> +++ b/drivers/mux/mux-gpio.c
> @@ -18,6 +18,8 @@
> #include <linux/platform_device.h>
> #include <linux/property.h>
>
> +#include "mux-gpio.h"
> +
> struct mux_gpio {
> struct gpio_descs *gpios;
> int *val;
> @@ -48,24 +50,21 @@ static const struct of_device_id mux_gpio_dt_ids[] = {
> };
> MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
>
> -static int mux_gpio_probe(struct platform_device *pdev)
> +struct mux_chip *mux_gpio_alloc(struct device *dev)
> {
> - struct device *dev = &pdev->dev;
> - struct device_node *np = dev->of_node;
> struct mux_chip *mux_chip;
> struct mux_gpio *mux_gpio;
> int pins;
> - u32 idle_state;
> int ret;
>
> pins = gpiod_count(dev, "mux");
> if (pins < 0)
> - return pins;
> + return ERR_PTR(pins);
>
> mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
> pins * sizeof(*mux_gpio->val));
> if (!mux_chip)
> - return -ENOMEM;
> + return ERR_PTR(-ENOMEM);
>
> mux_gpio = mux_chip_priv(mux_chip);
> mux_gpio->val = (int *)(mux_gpio + 1);
> @@ -76,11 +75,26 @@ static int mux_gpio_probe(struct platform_device *pdev)
> ret = PTR_ERR(mux_gpio->gpios);
> if (ret != -EPROBE_DEFER)
> dev_err(dev, "failed to get gpios\n");
> - return ret;
> + return ERR_PTR(ret);
> }
> WARN_ON(pins != mux_gpio->gpios->ndescs);
> mux_chip->mux->states = 1 << pins;
>
> + return mux_chip;
> +}
> +EXPORT_SYMBOL_GPL(mux_gpio_alloc);
> +
> +static int mux_gpio_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct mux_chip *mux_chip;
> + u32 idle_state;
> + int ret;
> +
> + mux_chip = mux_gpio_alloc(dev);
> + if (IS_ERR(mux_chip))
> + return PTR_ERR(mux_chip);
> +
> ret = device_property_read_u32(dev, "idle-state", &idle_state);
> if (ret >= 0) {
> if (idle_state >= mux_chip->mux->states) {
> diff --git a/drivers/mux/mux-gpio.h b/drivers/mux/mux-gpio.h
> new file mode 100644
> index 000000000000..fe3e8d0173aa
> --- /dev/null
> +++ b/drivers/mux/mux-gpio.h
> @@ -0,0 +1,13 @@
> +/*
> + * GPIO-controlled multiplexer driver interface
> + *
> + * Copyright (C) 2017 Axentia Technologies AB
> + *
> + * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +struct mux_chip *mux_gpio_alloc(struct device *dev);
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RESEND PATCH 1/2] Documentation: devicetree: Add i2c binding for mediatek MT2701 Soc Platform
From: Rob Herring @ 2017-01-21 20:22 UTC (permalink / raw)
To: Jun Gao
Cc: devicetree, srv_heupstream, Wolfram Sang, linux-kernel,
linux-mediatek, linux-i2c, Matthias Brugger, linux-arm-kernel
In-Reply-To: <1484732461-13594-2-git-send-email-jun.gao@mediatek.com>
On Wed, Jan 18, 2017 at 05:41:00PM +0800, Jun Gao wrote:
> From: Jun Gao <jun.gao@mediatek.com>
>
> This add i2c DT binding to i2c-mt6577.txt for MT2701.
>
> Signed-off-by: Jun Gao <jun.gao@mediatek.com>
> ---
> .../devicetree/bindings/i2c/i2c-mt6577.txt | 11 ++++++-----
> 1 file changed, 6 insertions(+), 5 deletions(-)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH 2/2] i2c: pca9541: Export OF device ID table as module aliases
From: Peter Rosin @ 2017-01-21 15:56 UTC (permalink / raw)
To: Javier Martinez Canillas, linux-kernel
Cc: linux-i2c, Guenter Roeck, Wolfram Sang
In-Reply-To: <1484574895-952-2-git-send-email-javier@osg.samsung.com>
On 2017-01-16 14:54, Javier Martinez Canillas wrote:
> The I2C core always reports a MODALIAS of the form i2c:<foo> even if the
> device was registered via OF, this means that exporting the OF device ID
> table device aliases in the module is not needed. But in order to change
> how the core reports modaliases to user-space, it's better to export it.
>
> Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
Acked-by: Peter Rosin <peda@axentia.se>
> ---
>
> drivers/i2c/muxes/i2c-mux-pca9541.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
> index 4ea7e691afc7..77840f7845a1 100644
> --- a/drivers/i2c/muxes/i2c-mux-pca9541.c
> +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
> @@ -90,6 +90,7 @@ static const struct of_device_id pca9541_of_match[] = {
> { .compatible = "nxp,pca9541" },
> {}
> };
> +MODULE_DEVICE_TABLE(of, pca9541_of_match);
> #endif
>
> /*
>
^ permalink raw reply
* Re: [PATCH 1/2] i2c: mux: pca954x: Export OF device ID table as module aliases
From: Peter Rosin @ 2017-01-21 15:56 UTC (permalink / raw)
To: Javier Martinez Canillas, linux-kernel; +Cc: Wolfram Sang, linux-i2c
In-Reply-To: <1484574895-952-1-git-send-email-javier@osg.samsung.com>
On 2017-01-16 14:54, Javier Martinez Canillas wrote:
> The I2C core always reports a MODALIAS of the form i2c:<foo> even if the
> device was registered via OF, this means that exporting the OF device ID
> table device aliases in the module is not needed. But in order to change
> how the core reports modaliases to user-space, it's better to export it.
>
> Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
Acked-by: Peter Rosin <peda@axentia.se>
> ---
>
> drivers/i2c/muxes/i2c-mux-pca954x.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
> index dd18b9ccb1f4..a0232e079545 100644
> --- a/drivers/i2c/muxes/i2c-mux-pca954x.c
> +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
> @@ -148,6 +148,7 @@ static const struct of_device_id pca954x_of_match[] = {
> { .compatible = "nxp,pca9548", .data = &chips[pca_9548] },
> {}
> };
> +MODULE_DEVICE_TABLE(of, pca954x_of_match);
> #endif
>
> /* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
>
^ permalink raw reply
* Re: [PATCH v1 3/3] i2c: zx2967: add i2c controller driver for ZTE's zx2967 family
From: Andy Shevchenko @ 2017-01-20 21:45 UTC (permalink / raw)
To: Baoyou Xie
Cc: jun.nie, Wolfram Sang, Rob Herring, Mark Rutland,
linux-arm Mailing List, linux-i2c, devicetree,
linux-kernel@vger.kernel.org, shawnguo, xie.baoyou, chen.chaokai,
wang.qiang01
In-Reply-To: <1484901827-16369-3-git-send-email-baoyou.xie@linaro.org>
On Fri, Jan 20, 2017 at 10:43 AM, Baoyou Xie <baoyou.xie@linaro.org> wrote:
> This patch adds i2c controller driver for ZTE's zx2967 family.
>
> +config I2C_ZX2967
> + bool "ZTE zx2967 I2C support"
No module?
> + depends on ARCH_ZX
> + default y
Would architecture selects it instead?
> + help
> + Selecting this option will add ZX I2C driver.
> + If unsure, say y.
Two sentences about the same in spite 'default y'.
> +#define BW1 (u32)1
Do you really need casting?
> +#define I2C_STOP 0
> +#define I2C_MASTER (1 << 0)
BIT(0) ?
And so further.
> +#define I2C_INT_MASK 0x3f
GENMASK(6, 0) ?
> +#define I2C_ERROR_MASK (I2C_ERROR_DATA | I2C_ERROR_DEVICE)
Ditto.
GENMASK() with explicit numbers.
> +static int zx2967_i2c_flush_fifos(struct zx2967_i2c_info *zx_i2c)
> +{
> + u32 val = 0;
> + u32 offset = 0;
Useless assignments.
> +
> + if (zx_i2c->msg_rd) {
> + offset = REG_RDCONF;
> + val = I2C_RFIFO_RESET;
> + } else {
> + offset = REG_WRCONF;
> + val = I2C_WFIFO_RESET;
> + }
> +static int zx2967_i2c_empty_rx_fifo(struct zx2967_i2c_info *zx_i2c, u32 size)
> +{
> + u8 val[I2C_FIFO_MAX] = {0};
> + int i = 0;
Ditto.
> +
> + if (size == 0) {
Would it be the case?
> + dev_err(zx_i2c->dev, "size is invalid\n");
> + return -EINVAL;
> + }
And what happens even in that case if you just remove this check?
> +
> + zx2967_i2c_readsb(zx_i2c, val, REG_DATA, size);
> + for (i = 0; i < size; i++) {
> + *(zx_i2c->buf++) = val[i];
> + zx_i2c->residue--;
> + if (zx_i2c->residue <= 0)
> + break;
> +static int zx2967_i2c_fill_tx_fifo(struct zx2967_i2c_info *zx_i2c)
> +{
> + u8 *buf = zx_i2c->buf;
> + size_t residue = zx_i2c->residue;
> +
> + if (residue == 0) {
> + dev_err(zx_i2c->dev, "residue is %d\n", (int)residue);
> + return -EINVAL;
> + }
Ditto.
> +static void zx2967_enable_tenbit(struct zx2967_i2c_info *zx_i2c, __u16 addr)
> +{
> + u16 val = ((addr >> 7) & 0x7);
Redundant parens.
> + if (val > 0) {
if (!val)
return;
> +static int zx2967_i2c_xfer_msg(struct zx2967_i2c_info *zx_i2c,
> + struct i2c_msg *msg
> +{
> + unsigned long time_left = 0;
> + unsigned int i = 0;
Redundant assignment.
> +
> + if (msg->len == 0)
if (!msg...)
> + return -EINVAL;
> +static int zx2967_i2c_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs, int num)
> +{
> + int i;
> + int ret = 0;
Redundant. See below.
> + struct zx2967_i2c_info *zx_i2c = i2c_get_adapdata(adap);
Reverse tree, please.
> +
> + if (zx_i2c->is_suspended)
> + return -EBUSY;
> +
> + zx2967_i2c_writel(zx_i2c, (msgs->addr & 0x7f), REG_DEVADDR_L);
> + zx2967_i2c_writel(zx_i2c, (msgs->addr >> 7) & 0x7, REG_DEVADDR_H);
> + if (zx2967_i2c_readl(zx_i2c, REG_DEVADDR_H) > 0)
> + zx2967_enable_tenbit(zx_i2c, msgs->addr);
> +
> + for (i = 0; i < num; i++) {
int ret;
> + ret = zx2967_i2c_xfer_msg(zx_i2c, &msgs[i]);
> + if (num > 1)
> + usleep_range(1000, 2000);
> + if (ret)
> + break;
return ret;
> + }
> +
> + return ret ?: i;
return i;
And looking now, how i can't be num here?
> +}
> +{
> + struct zx2967_i2c_info *zx_i2c = i2c_get_adapdata(adap);
> + u32 val;
> + u8 buf[2];
> + unsigned long time_left;
Reverse tree.
> + usleep_range(1000, 2000);
> + switch (size) {
> + case I2C_SMBUS_BYTE:
> + case I2C_SMBUS_BYTE_DATA:
> + val = zx2967_i2c_readl(zx_i2c, REG_DATA);
> + data->byte = val;
> + break;
> + case I2C_SMBUS_WORD_DATA:
> + case I2C_SMBUS_PROC_CALL:
> + buf[0] = zx2967_i2c_readl(zx_i2c, REG_DATA);
> + buf[1] = zx2967_i2c_readl(zx_i2c, REG_DATA);
> + data->word = (buf[0] << 8) | buf[1];
> + break;
> + default:
> + dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
I would revisit those several loooong functions for splitting/refactoring.
> +
> +static u32 zx2967_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
> + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
> + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
> + I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
> +}
It's just
#define XXX YY1 | YY2 | ...
> +#ifdef CONFIG_PM
> +static int zx2967_i2c_suspend(struct device *dev)
Switch to __maybe_unuse.
> +static int zx2967_i2c_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct zx2967_i2c_info *zx_i2c = NULL;
Redundant assignment.
> + struct clk *clk;
> + void __iomem *reg_base;
Reverse tree for all above until top of this function.
> + int ret = 0;
Redundant assignment.
> +
> + zx_i2c = devm_kzalloc(&pdev->dev, sizeof(*zx_i2c), GFP_KERNEL);
> + if (!zx_i2c)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "missing io resource\n");
> + return -EINVAL;
> + }
> +
Completely redundant lines of code.
> + reg_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(reg_base))
> + return PTR_ERR(reg_base);
> +
> + clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(clk)) {
> + dev_err(&pdev->dev, "missing controller clock");
> + return PTR_ERR(clk);
> + }
> +
> + ret = clk_prepare_enable(clk);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to enable i2c_clk\n");
> + return ret;
> + }
> +
> + of_property_read_u32(pdev->dev.of_node,
> + "clock-frequency", &zx_i2c->clk_freq);
devce_property_read_uXX();
> + zx_i2c->reg_base = reg_base;
> + zx_i2c->clk = clk;
> + zx_i2c->irq = platform_get_irq(pdev, 0);
Is this optional?
Otherwise has to be checked.
> + zx_i2c->id = pdev->id;
> + zx_i2c->dev = &pdev->dev;
> +
> + zx_i2c->pin_scl = devm_pinctrl_get_select(&pdev->dev, "scl");
> + if (IS_ERR(zx_i2c->pin_scl))
> + dev_info(&pdev->dev, "scl pin is not specified in dts\n");
> +
> + zx_i2c->pin_sda = devm_pinctrl_get_select(&pdev->dev, "sda");
> + if (IS_ERR(zx_i2c->pin_sda))
> + dev_info(&pdev->dev, "sda pin is not specified in dts\n");
> +
> + spin_lock_init(&zx_i2c->lock);
> + init_completion(&zx_i2c->complete);
> + platform_set_drvdata(pdev, zx_i2c);
> +
> + ret = zx2967_i2c_reset_hardware(zx_i2c);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to initialize i2c controller\n");
> + goto unprepare_clk;
> + }
> +
> + ret = devm_request_irq(&pdev->dev, zx_i2c->irq,
> + zx2967_i2c_isr, 0, dev_name(&pdev->dev), zx_i2c);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request irq %i\n", zx_i2c->irq);
> + goto unprepare_clk;
> + }
> +
> + i2c_set_adapdata(&zx_i2c->adap, zx_i2c);
> + zx_i2c->adap.owner = THIS_MODULE;
Do you need this?
> + zx_i2c->adap.class = I2C_CLASS_DEPRECATED;
> + strlcpy(zx_i2c->adap.name, "zx2967 i2c adapter",
> + sizeof(zx_i2c->adap.name));
> + zx_i2c->adap.algo = &zx2967_i2c_algo;
> + zx_i2c->adap.dev.parent = &pdev->dev;
> + zx_i2c->adap.nr = pdev->id;
> + zx_i2c->adap.dev.of_node = pdev->dev.of_node;
> +
> + ret = i2c_add_numbered_adapter(&zx_i2c->adap);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to add zx2967 i2c adapter\n");
> + goto unprepare_clk;
> + }
> +
> + return 0;
> +
> +unprepare_clk:
err_clk_unprepare:
> + clk_unprepare(zx_i2c->clk);
Perhaps clk_disable_unprepare();
> + return ret;
> +}
> +
> +static int zx2967_i2c_remove(struct platform_device *pdev)
> +{
> + struct zx2967_i2c_info *zx_i2c = platform_get_drvdata(pdev);
> +
> + i2c_del_adapter(&zx_i2c->adap);
> + clk_unprepare(zx_i2c->clk);
Ditto.
> +
> + return 0;
> +}
> +MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
> +MODULE_DESCRIPTION("ZTE zx2967 I2C Bus Controller driver");
> +MODULE_LICENSE("GPL v2");
When bool in Kconfig this all redundant.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: nested I2C muxes
From: Adrian Fiergolski @ 2017-01-20 8:50 UTC (permalink / raw)
To: Peter Rosin, linux-i2c
In-Reply-To: <ddbc9c2d-c1f4-0858-4453-bb03777ab236@axentia.se>
Hi Peter,
Thank you for your answers.
Yes, the typos you noticed (<5>, the same switch address) , are just
because I didn't prepare an example device tree carefully enough.
I will debug further from where my problem comes from.
Regards,
Adrian
On 19.01.2017 at 23:50, Peter Rosin wrote:
> On 2017-01-19 19:34, Peter Rosin wrote:
>> On 2017-01-19 19:03, Adrian Fiergolski wrote:
>>> Hi,
>>>
>>> I haven't found any information regarding support for nested i2c muxes.
>>> Is the below device tree supported by the current driver ?
>> No, it is not since you cannot have two devices with address 0x74
>> visible at the same time. You would have to set a different address
>> on one of the muxes using its A0-A2 pins. Then it should work.
>>
>> Look in Documentation/i2c/i2c-topology for more information.
>>
>> Cheers,
>> peda
>>
>>> &i2c0 {
>>> status = "okay";
>>> clock-frequency = <400000>;
>>> pinctrl-names = "default";
>>> pinctrl-0 = <&pinctrl_i2c0_default>;
>>>
>>> i2cswitch@74 {
>>> compatible = "nxp,pca9548";
>>> #address-cells = <1>;
>>> #size-cells = <0>;
>>> reg = <0x74>;
>>>
>>> i2c@0 {
>>> #address-cells = <1>;
>>> #size-cells = <0>;
>>> reg = <0>;
>>> si570: clock-generator@5d {
>>> #clock-cells = <0>;
>>> compatible = "silabs,si570";
>>> temperature-stability = <50>;
>>> reg = <0x5d>;
>>> factory-fout = <156250000>;
>>> clock-frequency = <148500000>;
>>> };
>>> };
>>>
>>> i2c@2 {
>>> #address-cells = <1>;
>>> #size-cells = <0>;
>>> reg = <5>;
> Also, this <5> is bogus. It should match the @2 a few lines up.
>
> Cheers,
> peda
>
>>> i2cswitch_hpc@74 {
>>> compatible = "nxp,pca9548";
>>> #address-cells = <1>;
>>> #size-cells = <0>;
>>> reg = <0x74>;
>>> hpc_caribou_sources_enable@76 {
>>> compatible= "nxp,pca9539";
>>> #address-cells = <1>;
>>> #size-cells = <0>;
>>> reg = <0x76>;
>>> };
>>> };
>>>
>>> };
>>>
>>> };
>>>
>>> };
>>>
>>> With such tree I am getting error
>>>
>>> [2.173385] i2c i2c-2: of_i2c: invalid addr=0 on
>>> /amba/i2c@e0004000/i2cswitch@74/i2c@2/i2cswitch_hpc@74
>>>
>>>
>>> Regards,
>>>
>>> Adrian Fiergolski
>>>
>>>
>>> --
>>> 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
>>>
>> --
>> 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
>>
> --
> 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
^ permalink raw reply
* [PATCH v1 3/3] i2c: zx2967: add i2c controller driver for ZTE's zx2967 family
From: Baoyou Xie @ 2017-01-20 8:43 UTC (permalink / raw)
To: jun.nie, wsa, robh+dt, mark.rutland
Cc: devicetree, xie.baoyou, linux-kernel, chen.chaokai, linux-i2c,
wang.qiang01, baoyou.xie, shawnguo, linux-arm-kernel
In-Reply-To: <1484901827-16369-1-git-send-email-baoyou.xie@linaro.org>
This patch adds i2c controller driver for ZTE's zx2967 family.
Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
---
drivers/i2c/busses/Kconfig | 8 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-zx2967.c | 645 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 654 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-zx2967.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e4a603e..946d2b0 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1246,4 +1246,12 @@ config I2C_OPAL
This driver can also be built as a module. If so, the module will be
called as i2c-opal.
+config I2C_ZX2967
+ bool "ZTE zx2967 I2C support"
+ depends on ARCH_ZX
+ default y
+ help
+ Selecting this option will add ZX I2C driver.
+ If unsure, say y.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index beb4809..16b2901 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
+obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c
new file mode 100644
index 0000000..6c2145b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-zx2967.c
@@ -0,0 +1,645 @@
+/*
+ * ZTE's zx2967 family i2c bus controller driver
+ *
+ * Copyright (C) 2017 ZTE Ltd.
+ *
+ * Author: Baoyou Xie <baoyou.xie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#define BW1 (u32)1
+
+#define REG_CMD 0x04
+#define REG_DEVADDR_H 0x0C
+#define REG_DEVADDR_L 0x10
+#define REG_CLK_DIV_FS 0x14
+#define REG_CLK_DIV_HS 0x18
+#define REG_WRCONF 0x1C
+#define REG_RDCONF 0x20
+#define REG_DATA 0x24
+#define REG_STAT 0x28
+
+#define I2C_STOP 0
+#define I2C_MASTER (1 << 0)
+#define I2C_ADDR_MODE_TEN (1 << 1)
+#define I2C_IRQ_MSK_ENABLE (1 << 3)
+#define I2C_RW_READ (1 << 4)
+#define I2C_CMB_RW_EN (1 << 5)
+#define I2C_START (1 << 6)
+#define I2C_ADDR_MODE_TEN (1 << 1)
+
+#define I2C_WFIFO_RESET (1 << 7)
+#define I2C_RFIFO_RESET (1 << 7)
+
+#define I2C_IRQ_ACK_CLEAR (1 << 7)
+#define I2C_INT_MASK 0x3f
+
+#define I2C_TRANS_DONE (1 << 0)
+#define I2C_ERROR_DEVICE (1 << 1)
+#define I2C_ERROR_DATA (1 << 2)
+#define I2C_ERROR_MASK (I2C_ERROR_DATA | I2C_ERROR_DEVICE)
+
+#define I2C_SR_BUSY (1 << 6)
+
+#define I2C_SR_EDEVICE (1 << 1)
+#define I2C_SR_EDATA (1 << 2)
+
+#define I2C_FIFO_MAX 16
+
+#define I2C_TIMEOUT msecs_to_jiffies(1000)
+
+struct zx2967_i2c_info {
+ spinlock_t lock;
+ struct device *dev;
+ struct i2c_adapter adap;
+ struct clk *clk;
+ struct completion complete;
+ u32 clk_freq;
+ struct pinctrl *pin_scl;
+ struct pinctrl *pin_sda;
+ void __iomem *reg_base;
+ size_t residue;
+ int id;
+ int irq;
+ int msg_rd;
+ u8 *buf;
+ u8 access_cnt;
+ bool is_suspended;
+};
+
+static void zx2967_i2c_writel(struct zx2967_i2c_info *zx_i2c,
+ u32 val, unsigned long reg)
+{
+ writel_relaxed(val, zx_i2c->reg_base + reg);
+}
+
+static u32 zx2967_i2c_readl(struct zx2967_i2c_info *zx_i2c, unsigned long reg)
+{
+ return readl_relaxed(zx_i2c->reg_base + reg);
+}
+
+static void zx2967_i2c_writesb(struct zx2967_i2c_info *zx_i2c,
+ void *data, unsigned long reg, int len)
+{
+ writesb(zx_i2c->reg_base + reg, data, len);
+}
+
+static void zx2967_i2c_readsb(struct zx2967_i2c_info *zx_i2c,
+ void *data, unsigned long reg, int len)
+{
+ readsb(zx_i2c->reg_base + reg, data, len);
+}
+
+static void zx2967_i2c_start_ctrl(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 status;
+ u32 ctl;
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT);
+ status |= I2C_IRQ_ACK_CLEAR;
+ zx2967_i2c_writel(zx_i2c, status, REG_STAT);
+
+ ctl = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ if (zx_i2c->msg_rd)
+ ctl |= I2C_RW_READ;
+ else
+ ctl &= ~I2C_RW_READ;
+ ctl &= ~I2C_CMB_RW_EN;
+ ctl |= I2C_START;
+ zx2967_i2c_writel(zx_i2c, ctl, REG_CMD);
+}
+
+static int zx2967_i2c_flush_fifos(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 val = 0;
+ u32 offset = 0;
+
+ if (zx_i2c->msg_rd) {
+ offset = REG_RDCONF;
+ val = I2C_RFIFO_RESET;
+ } else {
+ offset = REG_WRCONF;
+ val = I2C_WFIFO_RESET;
+ }
+
+ val |= zx2967_i2c_readl(zx_i2c, offset);
+ zx2967_i2c_writel(zx_i2c, val, offset);
+
+ return 0;
+}
+
+static int zx2967_i2c_empty_rx_fifo(struct zx2967_i2c_info *zx_i2c, u32 size)
+{
+ u8 val[I2C_FIFO_MAX] = {0};
+ int i = 0;
+
+ if (size == 0) {
+ dev_err(zx_i2c->dev, "size is invalid\n");
+ return -EINVAL;
+ }
+
+ zx2967_i2c_readsb(zx_i2c, val, REG_DATA, size);
+ for (i = 0; i < size; i++) {
+ *(zx_i2c->buf++) = val[i];
+ zx_i2c->residue--;
+ if (zx_i2c->residue <= 0)
+ break;
+ }
+
+ barrier();
+
+ return 0;
+}
+
+static int zx2967_i2c_fill_tx_fifo(struct zx2967_i2c_info *zx_i2c)
+{
+ u8 *buf = zx_i2c->buf;
+ size_t residue = zx_i2c->residue;
+
+ if (residue == 0) {
+ dev_err(zx_i2c->dev, "residue is %d\n", (int)residue);
+ return -EINVAL;
+ }
+
+ if (residue <= I2C_FIFO_MAX) {
+ zx2967_i2c_writesb(zx_i2c, buf, REG_DATA, residue);
+
+ /* Again update before writing to FIFO to make sure isr sees. */
+ zx_i2c->residue = 0;
+ zx_i2c->buf = NULL;
+ } else {
+ zx2967_i2c_writesb(zx_i2c, buf, REG_DATA, I2C_FIFO_MAX);
+ zx_i2c->residue -= I2C_FIFO_MAX;
+ zx_i2c->buf += I2C_FIFO_MAX;
+ }
+
+ barrier();
+
+ return 0;
+}
+
+static int zx2967_i2c_reset_hardware(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 val;
+ u32 clk_div;
+ u32 status;
+
+ val = I2C_MASTER | I2C_IRQ_MSK_ENABLE;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ clk_div = clk_get_rate(zx_i2c->clk) / zx_i2c->clk_freq - 1;
+ zx2967_i2c_writel(zx_i2c, clk_div, REG_CLK_DIV_FS);
+ zx2967_i2c_writel(zx_i2c, clk_div, REG_CLK_DIV_HS);
+
+ zx2967_i2c_writel(zx_i2c, I2C_FIFO_MAX - 1, REG_WRCONF);
+ zx2967_i2c_writel(zx_i2c, I2C_FIFO_MAX - 1, REG_RDCONF);
+ zx2967_i2c_writel(zx_i2c, 1, REG_RDCONF);
+
+ if (zx2967_i2c_flush_fifos(zx_i2c))
+ return -ETIMEDOUT;
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT);
+ if (status & (I2C_SR_BUSY))
+ return -EBUSY;
+ if (status & (I2C_SR_EDEVICE | I2C_SR_EDATA))
+ return -EIO;
+
+ enable_irq(zx_i2c->irq);
+
+ return 0;
+}
+
+static void zx2967_i2c_isr_clr(struct zx2967_i2c_info *zx_i2c)
+{
+ u32 status;
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT);
+ status |= I2C_IRQ_ACK_CLEAR;
+ zx2967_i2c_writel(zx_i2c, status, REG_STAT);
+}
+
+static irqreturn_t zx2967_i2c_isr(int irq, void *dev_id)
+{
+ u32 status;
+ struct zx2967_i2c_info *zx_i2c = (struct zx2967_i2c_info *)dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&zx_i2c->lock, flags);
+
+ status = zx2967_i2c_readl(zx_i2c, REG_STAT) & I2C_INT_MASK;
+ zx2967_i2c_isr_clr(zx_i2c);
+
+ if (status & I2C_ERROR_MASK) {
+ spin_unlock_irqrestore(&zx_i2c->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ if (status & I2C_TRANS_DONE)
+ complete(&zx_i2c->complete);
+
+ spin_unlock_irqrestore(&zx_i2c->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void zx2967_enable_tenbit(struct zx2967_i2c_info *zx_i2c, __u16 addr)
+{
+ u16 val = ((addr >> 7) & 0x7);
+
+ if (val > 0) {
+ zx2967_i2c_writel(zx_i2c, val, REG_DEVADDR_H);
+ val = (zx2967_i2c_readl(zx_i2c, REG_CMD)) | I2C_ADDR_MODE_TEN;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+ }
+}
+
+static int zx2967_i2c_xfer_msg(struct zx2967_i2c_info *zx_i2c,
+ struct i2c_msg *msg)
+{
+ unsigned long time_left = 0;
+ unsigned int i = 0;
+
+ if (msg->len == 0)
+ return -EINVAL;
+
+ zx2967_i2c_flush_fifos(zx_i2c);
+
+ zx_i2c->buf = msg->buf;
+ zx_i2c->residue = msg->len;
+ zx_i2c->access_cnt = msg->len / I2C_FIFO_MAX;
+ zx_i2c->msg_rd = (msg->flags & I2C_M_RD);
+
+ if (zx_i2c->msg_rd) {
+ for (i = 0; i < zx_i2c->access_cnt; i++) {
+ reinit_completion(&zx_i2c->complete);
+ zx2967_i2c_writel(zx_i2c, I2C_FIFO_MAX - 1,
+ REG_RDCONF);
+ zx2967_i2c_start_ctrl(zx_i2c);
+
+ time_left = wait_for_completion_timeout(
+ &zx_i2c->complete, I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev,
+ "read i2c transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ zx2967_i2c_empty_rx_fifo(zx_i2c, I2C_FIFO_MAX);
+ }
+
+ if (zx_i2c->residue < I2C_FIFO_MAX) {
+ reinit_completion(&zx_i2c->complete);
+ zx2967_i2c_writel(zx_i2c, zx_i2c->residue - 1,
+ REG_RDCONF);
+ zx2967_i2c_start_ctrl(zx_i2c);
+ time_left = wait_for_completion_timeout(
+ &zx_i2c->complete, I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev,
+ "read i2c transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ zx2967_i2c_empty_rx_fifo(zx_i2c, zx_i2c->residue);
+ }
+
+ zx_i2c->residue = 0;
+ zx_i2c->access_cnt = 0;
+ } else {
+ reinit_completion(&zx_i2c->complete);
+ zx2967_i2c_fill_tx_fifo(zx_i2c);
+ zx2967_i2c_start_ctrl(zx_i2c);
+
+ for (i = 0; i < zx_i2c->access_cnt; i++) {
+ time_left = wait_for_completion_timeout(
+ &zx_i2c->complete, I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev,
+ "write i2c transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ if (zx_i2c->residue != 0) {
+ reinit_completion(&zx_i2c->complete);
+ zx2967_i2c_fill_tx_fifo(zx_i2c);
+ zx2967_i2c_start_ctrl(zx_i2c);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int zx2967_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ int i;
+ int ret = 0;
+ struct zx2967_i2c_info *zx_i2c = i2c_get_adapdata(adap);
+
+ if (zx_i2c->is_suspended)
+ return -EBUSY;
+
+ zx2967_i2c_writel(zx_i2c, (msgs->addr & 0x7f), REG_DEVADDR_L);
+ zx2967_i2c_writel(zx_i2c, (msgs->addr >> 7) & 0x7, REG_DEVADDR_H);
+ if (zx2967_i2c_readl(zx_i2c, REG_DEVADDR_H) > 0)
+ zx2967_enable_tenbit(zx_i2c, msgs->addr);
+
+ for (i = 0; i < num; i++) {
+ ret = zx2967_i2c_xfer_msg(zx_i2c, &msgs[i]);
+ if (num > 1)
+ usleep_range(1000, 2000);
+ if (ret)
+ break;
+ }
+
+ return ret ?: i;
+}
+
+static int zx2967_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data)
+{
+ struct zx2967_i2c_info *zx_i2c = i2c_get_adapdata(adap);
+ u32 val;
+ u8 buf[2];
+ unsigned long time_left;
+
+ val = zx2967_i2c_readl(zx_i2c, REG_RDCONF);
+ val |= I2C_RFIFO_RESET;
+ zx2967_i2c_writel(zx_i2c, val, REG_RDCONF);
+ zx2967_i2c_writel(zx_i2c, (addr & 0x7f), REG_DEVADDR_L);
+
+ zx2967_enable_tenbit(zx_i2c, addr);
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val &= ~I2C_RW_READ;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ read_write = I2C_SMBUS_WRITE;
+ break;
+ case I2C_SMBUS_BYTE:
+ zx2967_i2c_writel(zx_i2c, command, REG_DATA);
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ zx2967_i2c_writel(zx_i2c, command, REG_DATA);
+
+ if (read_write == I2C_SMBUS_WRITE)
+ zx2967_i2c_writel(zx_i2c, data->byte, REG_DATA);
+
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ zx2967_i2c_writel(zx_i2c, command, REG_DATA);
+ zx2967_i2c_writel(zx_i2c, (data->word & 0xff), REG_DATA);
+
+ if (read_write == I2C_SMBUS_WRITE) {
+ zx2967_i2c_writel(zx_i2c, (data->word >> 8), REG_DATA);
+ zx2967_i2c_writel(zx_i2c,
+ (data->word & 0xff), REG_DATA);
+ }
+ break;
+
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ if (read_write == I2C_SMBUS_WRITE) {
+ reinit_completion(&zx_i2c->complete);
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val |= I2C_START;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ time_left = wait_for_completion_timeout(
+ &zx_i2c->complete, I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev, "i2c write transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ return 0;
+ }
+
+ reinit_completion(&zx_i2c->complete);
+
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val |= I2C_CMB_RW_EN;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ val = zx2967_i2c_readl(zx_i2c, REG_CMD);
+ val |= I2C_START;
+ zx2967_i2c_writel(zx_i2c, val, REG_CMD);
+
+ time_left = wait_for_completion_timeout(&zx_i2c->complete,
+ I2C_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(zx_i2c->dev, "i2c read transfer timed out\n");
+ disable_irq(zx_i2c->irq);
+ zx2967_i2c_reset_hardware(zx_i2c);
+ return -EIO;
+ }
+
+ usleep_range(1000, 2000);
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ val = zx2967_i2c_readl(zx_i2c, REG_DATA);
+ data->byte = val;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ buf[0] = zx2967_i2c_readl(zx_i2c, REG_DATA);
+ buf[1] = zx2967_i2c_readl(zx_i2c, REG_DATA);
+ data->word = (buf[0] << 8) | buf[1];
+ break;
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static u32 zx2967_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
+}
+
+#ifdef CONFIG_PM
+static int zx2967_i2c_suspend(struct device *dev)
+{
+ struct zx2967_i2c_info *zx_i2c = dev_get_drvdata(dev);
+
+ zx_i2c->is_suspended = true;
+ if (__clk_get_enable_count(zx_i2c->clk) == 1)
+ clk_disable_unprepare(zx_i2c->clk);
+
+ return 0;
+}
+
+static int zx2967_i2c_resume(struct device *dev)
+{
+ struct zx2967_i2c_info *zx_i2c = dev_get_drvdata(dev);
+
+ zx_i2c->is_suspended = false;
+ clk_prepare_enable(zx_i2c->clk);
+
+ return 0;
+}
+static const struct dev_pm_ops zx2967_i2c_dev_pm_ops = {
+ .suspend = zx2967_i2c_suspend,
+ .resume = zx2967_i2c_resume,
+};
+#define ZX2967_I2C_DEV_PM_OPS (&zx2967_i2c_dev_pm_ops)
+#else
+#define ZX2967_I2C_DEV_PM_OPS NULL
+#endif
+
+static const struct i2c_algorithm zx2967_i2c_algo = {
+ .master_xfer = zx2967_i2c_xfer,
+ .smbus_xfer = zx2967_smbus_xfer,
+ .functionality = zx2967_i2c_func,
+};
+
+static const struct of_device_id zx2967_i2c_of_match[] = {
+ { .compatible = "zte,zx296718-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, zx2967_i2c_of_match);
+
+static int zx2967_i2c_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct zx2967_i2c_info *zx_i2c = NULL;
+ struct clk *clk;
+ void __iomem *reg_base;
+ int ret = 0;
+
+ zx_i2c = devm_kzalloc(&pdev->dev, sizeof(*zx_i2c), GFP_KERNEL);
+ if (!zx_i2c)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing io resource\n");
+ return -EINVAL;
+ }
+
+ reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg_base))
+ return PTR_ERR(reg_base);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "missing controller clock");
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable i2c_clk\n");
+ return ret;
+ }
+
+ of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &zx_i2c->clk_freq);
+ zx_i2c->reg_base = reg_base;
+ zx_i2c->clk = clk;
+ zx_i2c->irq = platform_get_irq(pdev, 0);
+ zx_i2c->id = pdev->id;
+ zx_i2c->dev = &pdev->dev;
+
+ zx_i2c->pin_scl = devm_pinctrl_get_select(&pdev->dev, "scl");
+ if (IS_ERR(zx_i2c->pin_scl))
+ dev_info(&pdev->dev, "scl pin is not specified in dts\n");
+
+ zx_i2c->pin_sda = devm_pinctrl_get_select(&pdev->dev, "sda");
+ if (IS_ERR(zx_i2c->pin_sda))
+ dev_info(&pdev->dev, "sda pin is not specified in dts\n");
+
+ spin_lock_init(&zx_i2c->lock);
+ init_completion(&zx_i2c->complete);
+ platform_set_drvdata(pdev, zx_i2c);
+
+ ret = zx2967_i2c_reset_hardware(zx_i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize i2c controller\n");
+ goto unprepare_clk;
+ }
+
+ ret = devm_request_irq(&pdev->dev, zx_i2c->irq,
+ zx2967_i2c_isr, 0, dev_name(&pdev->dev), zx_i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %i\n", zx_i2c->irq);
+ goto unprepare_clk;
+ }
+
+ i2c_set_adapdata(&zx_i2c->adap, zx_i2c);
+ zx_i2c->adap.owner = THIS_MODULE;
+ zx_i2c->adap.class = I2C_CLASS_DEPRECATED;
+ strlcpy(zx_i2c->adap.name, "zx2967 i2c adapter",
+ sizeof(zx_i2c->adap.name));
+ zx_i2c->adap.algo = &zx2967_i2c_algo;
+ zx_i2c->adap.dev.parent = &pdev->dev;
+ zx_i2c->adap.nr = pdev->id;
+ zx_i2c->adap.dev.of_node = pdev->dev.of_node;
+
+ ret = i2c_add_numbered_adapter(&zx_i2c->adap);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add zx2967 i2c adapter\n");
+ goto unprepare_clk;
+ }
+
+ return 0;
+
+unprepare_clk:
+ clk_unprepare(zx_i2c->clk);
+ return ret;
+}
+
+static int zx2967_i2c_remove(struct platform_device *pdev)
+{
+ struct zx2967_i2c_info *zx_i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&zx_i2c->adap);
+ clk_unprepare(zx_i2c->clk);
+
+ return 0;
+}
+
+static struct platform_driver zx2967_i2c_driver = {
+ .probe = zx2967_i2c_probe,
+ .remove = zx2967_i2c_remove,
+ .driver = {
+ .name = "zx2967_i2c",
+ .of_match_table = zx2967_i2c_of_match,
+ .pm = ZX2967_I2C_DEV_PM_OPS,
+ },
+};
+module_platform_driver(zx2967_i2c_driver);
+
+MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
+MODULE_DESCRIPTION("ZTE zx2967 I2C Bus Controller driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH v1 2/3] MAINTAINERS: add zx2967 i2c controller driver to ARM ZTE architecture
From: Baoyou Xie @ 2017-01-20 8:43 UTC (permalink / raw)
To: jun.nie, wsa, robh+dt, mark.rutland
Cc: devicetree, xie.baoyou, linux-kernel, chen.chaokai, linux-i2c,
wang.qiang01, baoyou.xie, shawnguo, linux-arm-kernel
In-Reply-To: <1484901827-16369-1-git-send-email-baoyou.xie@linaro.org>
Add the zx2967 i2c controller driver as maintained by ARM ZTE
architecture maintainers, as they're parts of the core IP.
Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 275c434..757c098 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1987,12 +1987,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-zx/
F: drivers/clk/zte/
+F: drivers/i2c/busses/i2c-zx2967.c
F: drivers/reset/reset-zx2967.c
F: drivers/soc/zte/
F: drivers/thermal/zx*
F: drivers/watchdog/zx2967_wdt.c
F: Documentation/devicetree/bindings/arm/zte.txt
F: Documentation/devicetree/bindings/clock/zx296702-clk.txt
+F: Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
F: Documentation/devicetree/bindings/reset/zte,zx2967-reset.txt
F: Documentation/devicetree/bindings/soc/zte/
F: Documentation/devicetree/bindings/thermal/zx*
--
2.7.4
^ permalink raw reply related
* [PATCH v1 1/3] dt: bindings: add documentation for zx2967 family i2c controller
From: Baoyou Xie @ 2017-01-20 8:43 UTC (permalink / raw)
To: jun.nie-QSEj5FYQhm4dnm+yROfE0A, wsa-z923LK4zBo2bacvFa/9K2g,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
baoyou.xie-QSEj5FYQhm4dnm+yROfE0A,
xie.baoyou-Th6q7B73Y6EnDS1+zs4M5A,
chen.chaokai-Th6q7B73Y6EnDS1+zs4M5A,
wang.qiang01-Th6q7B73Y6EnDS1+zs4M5A
This patch adds dt-binding documentation for zx2967 family
i2c controller.
Signed-off-by: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
.../devicetree/bindings/i2c/i2c-zx2967.txt | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt b/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
new file mode 100644
index 0000000..cb806d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt
@@ -0,0 +1,22 @@
+ZTE zx2967 I2C controller
+
+Required properties:
+ - compatible: must be "zte,zx296718-i2c"
+ - reg: physical address and length of the device registers
+ - interrupts: a single interrupt specifier
+ - clocks: clock for the device
+ - #address-cells: should be <1>
+ - #size-cells: should be <0>
+ - clock-frequency: the desired I2C bus clock frequency.
+
+Examples:
+
+ i2c@112000 {
+ compatible = "zte,zx296718-i2c";
+ reg = <0x00112000 0x1000>;
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&osc24m>;
+ #address-cells = <1>
+ #size-cells = <0>;
+ clock-frequency = <1600000>;
+ };
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v5 5/5] i2c: mux: pca954x: Add irq-mask-enable to delay enabling irqs
From: Peter Rosin @ 2017-01-19 22:56 UTC (permalink / raw)
To: Phil Reid, wsa-z923LK4zBo2bacvFa/9K2g,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <61c38233-abf2-df31-6f7c-c4214f4103aa-qgqNFa1JUf/o2iN0hyhwsIdd74u8MsAO@public.gmane.org>
On 2017-01-19 08:48, Phil Reid wrote:
> On 18/01/2017 20:19, Peter Rosin wrote:
>> On 2017-01-17 09:00, Phil Reid wrote:
>>> Unfortunately some hardware device will assert their irq line immediately
>>> on power on and provide no mechanism to mask the irq. As the i2c muxes
>>> provide no method to mask irq line this provides a work around by keeping
>>> the parent irq masked until enough device drivers have loaded to service
>>> all pending interrupts.
>>>
>>> For example the the ltc1760 assert its SMBALERT irq immediately on power
>>> on. With two ltc1760 attached to bus 0 & 1 on a pca954x mux when the first
>>> device is registered irq are enabled and fire continuously as the second
>>> device driver has not yet loaded. Setting this parameter to <1 1> will
>>> delay the irq being enabled until both devices are ready.
>>>
>>> Signed-off-by: Phil Reid <preid-qgqNFa1JUf/o2iN0hyhwsIdd74u8MsAO@public.gmane.org>
>>> ---
>>> drivers/i2c/muxes/i2c-mux-pca954x.c | 33 ++++++++++++++++++++++++++++++---
>>> 1 file changed, 30 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
>>> index f55da88..012b2ef 100644
>>> --- a/drivers/i2c/muxes/i2c-mux-pca954x.c
>>> +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
>>> @@ -76,6 +76,19 @@ struct chip_desc {
>>> } muxtype;
>>> };
>>>
>>> +/*
>>> + * irq_mask_enable: Provides a mechanism to work around hardware that asserts
>>> + * their irq immediately on power on. It allows the enabling of the irq to be
>>> + * delayed until the corresponding bits in the the irq_mask are set thru
>>> + * irq_unmask.
>>> + * For example the ltc1760 assert its SMBALERT irq immediately on power on.
>>> + * With two ltc1760 attached to bus 0 & 1 on a pca954x mux when the first
>>> + * device is registered irq are enabled and fire continuously as the second
>>> + * device driver has not yet loaded. Setting this parameter to 0x3 while
>>> + * delay the irq being enabled until both devices are ready.
>>> + * This workaround will not work if two devices share an interrupt on the
>>> + * same bus segment.
>>
>> It will also not work if something shares the interrupt with the pca954x mux,
>> on the parent side of the mux, so to speak. Then that other driver may
>> potentially enable the irq "behind the back" of the pca954x driver.
>>
>>> + */
>>> struct pca954x {
>>> const struct chip_desc *chip;
>>>
>>> @@ -84,7 +97,9 @@ struct pca954x {
>>> struct i2c_client *client;
>>>
>>> struct irq_domain *irq;
>>> + unsigned int irq_mask_enable;
>>> unsigned int irq_mask;
>>> + bool irq_enabled;
>>> spinlock_t lock;
>>> };
>>>
>>> @@ -266,8 +281,10 @@ static void pca954x_irq_mask(struct irq_data *idata)
>>> spin_lock_irqsave(&data->lock, flags);
>>>
>>> data->irq_mask &= ~BIT(pos);
>>> - if (!data->irq_mask)
>>> + if (data->irq_enabled && !data->irq_mask) {
>>> disable_irq(data->client->irq);
>>> + data->irq_enabled = false;
>>> + }
>>
>> When irq_mask_enable is non-zero, I think the parent irq should be masked
>> when the first irq from the set in irq_mask_enable is masked. For symmetry.
>>
>> Like so (untested):
>>
>> if (data->irq_enabled) {
>> if (!data->irq_mask ||
>> (data->irq_mask & mask_enable) != mask_enable) {
>> disable_irq(data->client->irq);
>> data->irq_enabled = false;
>> }
>> }
> Yeap this make sense.
>
>>
>> Hmm, this whole thing is fiddly and while it solves your problem it doesn't
>> allow for solving the more general problem when there are "problematic"
>> devices mixed with other devices. At least, I don't see it. And the
>> limitations we are walking into with tracking number of enables etc suggests
>> that we are attacking this at the wrong level. Maybe you should try to work
>> around the hw limitations not in the pca954x driver, but in the irq core?
>
> I'm looking at the option of getting the hardware changed to not route
> the irq for my chips thru the i2c mux. Fortunately the hardware is going thru a
> revision for some other changes. Messing with the irq core sounds dangerous
> with my level of knowledge.
Yeah, but I bet you'd get some attention from people with more irq
experience. That can't be bad :-)
> The other way I think I can tackle it after reading the datasheet for the ltc1760 is that
> it'll deassert it's irq (smbalert) line when the host sends a ARA request on the bus segment.
> There's a driver in the kernel for this already, but it's not DT enable and doesn't
> handle multiple bus segments. I'll have a look at that as well.
> Pretty sure it would need the mux to become an irq parent as per patch 1-3 of this series.
> This would be so the system can figure out which segment to do the poll on.
Yeah sounds neater. It has the slight drawback that it may not work
for pure i2c buses since it an SMB thing??
BTW, why do you need special treatment for multiple segments? Will it not
simply have an ARA appear on whatever i2c bus the device sits on? And if
something requests to send an ARA message on a bus that happens to be a
muxed segment, my mental picture is that the mux will be operated as usual
so that the ARA appears on the muxed segment. Maybe I'm missing something?
> But p4-5 could be dropped which is where we're stuck I think.
Yes, I dislike to add a workaround for a specific case that might get
in the way for anybody wishing to fix a bigger, more generic, problem...
> Looking at this approach it shouldn't matter if the ltc1760 driver has registered yet or not.
> This approach possibly has a lot more generic appeal I think.
>
> Thoughts on just submitting p1-3 for now while I figure out the SMB alert approach?
Yes, looks like a plan. Thanks in advance!
>> I.e. have the irq core check, for each irq, for a property that specifies
>> the depth at which each irq should be unmasked. This new property should
>> probably be located in the interrupt-controller node? Then the code can
>> unmask interrupts when the depth hits this mark, instead of always unmasking
>> the interrupt when the depth changes from zero to one. You are then adding
>> the workaround at a level where there is enough information to fix the
>> more general problem. I think?
>>
>> But, once again, I'm no irq expert and would desperately like a second
>> opinion on this stuff...
>>
>>>
>>> spin_unlock_irqrestore(&data->lock, flags);
>>> }
>>> @@ -275,14 +292,18 @@ static void pca954x_irq_mask(struct irq_data *idata)
>>> static void pca954x_irq_unmask(struct irq_data *idata)
>>> {
>>> struct pca954x *data = irq_data_get_irq_chip_data(idata);
>>> + unsigned int mask_enable = data->irq_mask_enable;
>>> unsigned int pos = idata->hwirq;
>>> unsigned long flags;
>>>
>>> spin_lock_irqsave(&data->lock, flags);
>>>
>>> - if (!data->irq_mask)
>>> - enable_irq(data->client->irq);
>>> data->irq_mask |= BIT(pos);
>>> + if (!data->irq_enabled
>>> + && (data->irq_mask & mask_enable) == mask_enable) {
>>
>> I think the coding standard says that the && should be at the end of the
>> first line. Didn't checkpatch complain?
>
> No it didn't complain. and I wasn't sure which way to do this.
Ah, you need the --strict option for that to show up...
Cheers,
peda
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: nested I2C muxes
From: Peter Rosin @ 2017-01-19 22:50 UTC (permalink / raw)
To: Adrian Fiergolski, linux-i2c
In-Reply-To: <693bca75-e610-a96e-45d8-06af861500a9@axentia.se>
On 2017-01-19 19:34, Peter Rosin wrote:
> On 2017-01-19 19:03, Adrian Fiergolski wrote:
>> Hi,
>>
>> I haven't found any information regarding support for nested i2c muxes.
>> Is the below device tree supported by the current driver ?
>
> No, it is not since you cannot have two devices with address 0x74
> visible at the same time. You would have to set a different address
> on one of the muxes using its A0-A2 pins. Then it should work.
>
> Look in Documentation/i2c/i2c-topology for more information.
>
> Cheers,
> peda
>
>> &i2c0 {
>> status = "okay";
>> clock-frequency = <400000>;
>> pinctrl-names = "default";
>> pinctrl-0 = <&pinctrl_i2c0_default>;
>>
>> i2cswitch@74 {
>> compatible = "nxp,pca9548";
>> #address-cells = <1>;
>> #size-cells = <0>;
>> reg = <0x74>;
>>
>> i2c@0 {
>> #address-cells = <1>;
>> #size-cells = <0>;
>> reg = <0>;
>> si570: clock-generator@5d {
>> #clock-cells = <0>;
>> compatible = "silabs,si570";
>> temperature-stability = <50>;
>> reg = <0x5d>;
>> factory-fout = <156250000>;
>> clock-frequency = <148500000>;
>> };
>> };
>>
>> i2c@2 {
>> #address-cells = <1>;
>> #size-cells = <0>;
>> reg = <5>;
Also, this <5> is bogus. It should match the @2 a few lines up.
Cheers,
peda
>> i2cswitch_hpc@74 {
>> compatible = "nxp,pca9548";
>> #address-cells = <1>;
>> #size-cells = <0>;
>> reg = <0x74>;
>> hpc_caribou_sources_enable@76 {
>> compatible= "nxp,pca9539";
>> #address-cells = <1>;
>> #size-cells = <0>;
>> reg = <0x76>;
>> };
>> };
>>
>> };
>>
>> };
>>
>> };
>>
>> With such tree I am getting error
>>
>> [2.173385] i2c i2c-2: of_i2c: invalid addr=0 on
>> /amba/i2c@e0004000/i2cswitch@74/i2c@2/i2cswitch_hpc@74
>>
>>
>> Regards,
>>
>> Adrian Fiergolski
>>
>>
>> --
>> 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
>>
>
> --
> 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
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox