* [PATCH 0/2] AS3935 lightning sensor support
From: Matt Ranostay @ 2014-02-06 7:00 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: matt.porter-QSEj5FYQhm4dnm+yROfE0A,
pantelis.antoniou-Re5JQEeQqe8AvxtiuMwx3w, Matt Ranostay
This series adds support for the AMS AS3935 lightning sensor that allows
reporting back estimated storm distance and strike events.
Matt Ranostay (2):
iio:as3935: Add DT binding docs for AS3935 driver
iio: Add AS3935 lightning sensor support
.../ABI/testing/sysfs-bus-iio-proximity-as3935 | 18 +
.../devicetree/bindings/iio/proximity/as3935.txt | 28 ++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/proximity/Kconfig | 19 +
drivers/iio/proximity/Makefile | 6 +
drivers/iio/proximity/as3935.c | 437 +++++++++++++++++++++
8 files changed, 511 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935
create mode 100644 Documentation/devicetree/bindings/iio/proximity/as3935.txt
create mode 100644 drivers/iio/proximity/Kconfig
create mode 100644 drivers/iio/proximity/Makefile
create mode 100644 drivers/iio/proximity/as3935.c
--
1.8.3.2
^ permalink raw reply
* [PATCH 1/2] iio:as3935: Add DT binding docs for AS3935 driver
From: Matt Ranostay @ 2014-02-06 7:00 UTC (permalink / raw)
To: linux-kernel, linux-iio, devicetree
Cc: matt.porter, pantelis.antoniou, Matt Ranostay
In-Reply-To: <1391670015-6551-1-git-send-email-mranostay@gmail.com>
Document compatible string, required and optional DT properties for
AS3935 chipset driver.
Signed-off-by: Matt Ranostay <mranostay@gmail.com>
---
.../devicetree/bindings/iio/proximity/as3935.txt | 28 ++++++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
2 files changed, 29 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/proximity/as3935.txt
diff --git a/Documentation/devicetree/bindings/iio/proximity/as3935.txt b/Documentation/devicetree/bindings/iio/proximity/as3935.txt
new file mode 100644
index 0000000..574d49c
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/proximity/as3935.txt
@@ -0,0 +1,28 @@
+Austrian Microsystems AS3935 Franklin lightning sensor device driver
+
+Required properties:
+ - compatible: must be "ams,as3935"
+ - reg: SPI chip select number for the device
+ - spi-cpha: SPI Mode 1. Refer to spi/spi-bus.txt for generic SPI
+ slave node bindings.
+ - interrupt-parent : should be the phandle for the interrupt controller
+ - interrupts : interrupt mapping for GPIO IRQ
+
+ Refer to interrupt-controller/interrupts.txt for generic
+ interrupt client node bindings.
+
+Optional properties:
+ - ams,tune-cap: Calibration tuning capacitor stepping value 0 - 15.
+ Range of 0 to 120 pF, 8pF steps. This will require using the
+ calibration data from the manufacturer.
+
+Example:
+
+as3935@0 {
+ compatible = "ams,as3935";
+ reg = <0>;
+ spi-cpha;
+ interrupt-parent = <&gpio1>;
+ interrupts = <16 1>;
+ ams,tune-cap = <10>;
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 3c31b40..9dd66ca 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -10,6 +10,7 @@ aeroflexgaisler Aeroflex Gaisler AB
ak Asahi Kasei Corp.
altr Altera Corp.
amcc Applied Micro Circuits Corporation (APM, formally AMCC)
+ams AMS AG
amstaos AMS-Taos Inc.
apm Applied Micro Circuits Corporation (APM)
arm ARM Ltd.
--
1.8.3.2
^ permalink raw reply related
* [PATCH 2/2] iio: Add AS3935 lightning sensor support
From: Matt Ranostay @ 2014-02-06 7:00 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: matt.porter-QSEj5FYQhm4dnm+yROfE0A,
pantelis.antoniou-Re5JQEeQqe8AvxtiuMwx3w, Matt Ranostay
In-Reply-To: <1391670015-6551-1-git-send-email-mranostay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
AS3935 chipset can detect lightning strikes and reports those back as
events and the estimated distance to the storm.
Signed-off-by: Matt Ranostay <mranostay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
.../ABI/testing/sysfs-bus-iio-proximity-as3935 | 18 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/proximity/Kconfig | 19 +
drivers/iio/proximity/Makefile | 6 +
drivers/iio/proximity/as3935.c | 437 +++++++++++++++++++++
6 files changed, 482 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935
create mode 100644 drivers/iio/proximity/Kconfig
create mode 100644 drivers/iio/proximity/Makefile
create mode 100644 drivers/iio/proximity/as3935.c
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935 b/Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935
new file mode 100644
index 0000000..f6d9e6f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935
@@ -0,0 +1,18 @@
+What /sys/bus/iio/devices/iio:deviceX/in_proximity_raw
+Date: January 2014
+KernelVersion: 3.15
+Contact: Matt Ranostay <mranostay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+Description:
+ Get the current distance in kilometers of storm
+ 1 = storm overhead
+ 1-40 = distance in kilometers
+ 63 = out of range
+
+What /sys/bus/iio/devices/iio:deviceX/gain_boost
+Date: January 2014
+KernelVersion: 3.15
+Contact: Matt Ranostay <mranostay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+Description:
+ Show or set the gain boost of the amp, from 0-31 range.
+ 18 = indoors (default)
+ 14 = outdoors
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 5dd0e12..743485e 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -74,6 +74,7 @@ if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
endif #IIO_TRIGGER
source "drivers/iio/pressure/Kconfig"
+source "drivers/iio/proximity/Kconfig"
source "drivers/iio/temperature/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 887d390..698afc2 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -24,5 +24,6 @@ obj-y += light/
obj-y += magnetometer/
obj-y += orientation/
obj-y += pressure/
+obj-y += proximity/
obj-y += temperature/
obj-y += trigger/
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
new file mode 100644
index 0000000..0c8cdf5
--- /dev/null
+++ b/drivers/iio/proximity/Kconfig
@@ -0,0 +1,19 @@
+#
+# Proximity sensors
+#
+
+menu "Lightning sensors"
+
+config AS3935
+ tristate "AS3935 Franklin lightning sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ depends on SPI
+ help
+ Say Y here to build SPI interface support for the Austrian
+ Microsystems AS3935 lightning detection sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called as3935
+
+endmenu
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
new file mode 100644
index 0000000..743adee
--- /dev/null
+++ b/drivers/iio/proximity/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO proximity sensors
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AS3935) += as3935.o
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
new file mode 100644
index 0000000..109759e
--- /dev/null
+++ b/drivers/iio/proximity/as3935.c
@@ -0,0 +1,437 @@
+/*
+ * as3935.c - Support for AS3935 Franklin lightning sensor
+ *
+ * Copyright (C) 2014 Matt Ranostay <mranostay-Re5JQEeQqe8AvxtiuMwx3w@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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_gpio.h>
+
+
+#define AS3935_AFE_GAIN 0x00
+#define AS3935_AFE_MASK 0x3F
+#define AS3935_AFE_GAIN_MAX 0x1F
+#define AS3935_AFE_PWR_BIT BIT(0)
+
+#define AS3935_INT 0x03
+#define AS3935_INT_MASK 0x07
+#define AS3935_EVENT_INT BIT(3)
+#define AS3935_NOISE_INT BIT(1)
+
+#define AS3935_DATA 0x07
+#define AS3935_DATA_MASK 0x3F
+
+#define AS3935_TUNE_CAP 0x08
+#define AS3935_CALIBRATE 0x3D
+
+#define AS3935_WRITE_DATA BIT(15)
+#define AS3935_READ_DATA BIT(14)
+#define AS3935_ADDRESS(x) (x<<8)
+
+struct as3935_state {
+ struct spi_device *spi;
+ struct iio_trigger *trig;
+ struct mutex lock;
+ struct delayed_work work;
+
+ u32 tune_cap;
+};
+
+static const struct iio_chan_spec as3935_channels[] = {
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 6,
+ .storagebits = 8,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int as3935_read(struct as3935_state *st, unsigned int reg, int *val)
+{
+ u8 tx, rx;
+ int ret;
+
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = &tx,
+ .bits_per_word = 8,
+ .len = 1,
+ }, {
+ .rx_buf = &rx,
+ .bits_per_word = 8,
+ .len = 1,
+ },
+ };
+ tx = (AS3935_READ_DATA | AS3935_ADDRESS(reg)) >> 8;
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ *val = rx;
+
+ return ret;
+};
+
+static int as3935_write(struct as3935_state *st,
+ unsigned int reg,
+ unsigned int val)
+{
+ u8 buf[2];
+
+ buf[0] = (AS3935_WRITE_DATA | AS3935_ADDRESS(reg)) >> 8;
+ buf[1] = val;
+
+ return spi_write(st->spi, (u8 *) &buf, 2);
+};
+
+static ssize_t as3935_gain_boost_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct as3935_state *st = iio_priv(dev_to_iio_dev(dev));
+ int val, ret;
+
+ ret = as3935_read(st, AS3935_AFE_GAIN, &val);
+ if (ret)
+ return ret;
+ val = (val & AS3935_AFE_MASK) >> 1;
+
+ return sprintf(buf, "%d\n", val);
+};
+
+static ssize_t as3935_gain_boost_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct as3935_state *st = iio_priv(dev_to_iio_dev(dev));
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul((const char *) buf, 10, &val);
+ if (ret)
+ return -EINVAL;
+
+ if (val > AS3935_AFE_GAIN_MAX)
+ return -EINVAL;
+
+ as3935_write(st, AS3935_AFE_GAIN, val << 1);
+
+ return len;
+};
+
+static IIO_DEVICE_ATTR(gain_boost, S_IRUGO | S_IWUSR,
+ as3935_gain_boost_show, as3935_gain_boost_store, 0);
+
+
+static struct attribute *as3935_attributes[] = {
+ &iio_dev_attr_gain_boost.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group as3935_attribute_group = {
+ .attrs = as3935_attributes,
+};
+
+static int as3935_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct as3935_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (m != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ *val2 = 0;
+ ret = as3935_read(st, AS3935_DATA, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info as3935_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &as3935_attribute_group,
+ .read_raw = &as3935_read_raw,
+};
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+};
+
+static irqreturn_t as3935_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct as3935_state *st = iio_priv(indio_dev);
+ int val, ret;
+
+ ret = as3935_read(st, AS3935_DATA, &val);
+ if (ret)
+ goto err_read;
+ val &= AS3935_DATA_MASK;
+ iio_push_to_buffers_with_timestamp(indio_dev, &val, iio_get_time_ns());
+err_read:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+};
+
+static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static void as3935_event_work(struct work_struct *work)
+{
+ struct as3935_state *st;
+ struct spi_device *spi;
+ int val;
+
+ st = container_of(work, struct as3935_state, work.work);
+ spi = st->spi;
+
+ as3935_read(st, AS3935_INT, &val);
+ val &= AS3935_INT_MASK;
+
+ switch (val) {
+ case AS3935_EVENT_INT:
+ iio_trigger_poll(st->trig, 0);
+ break;
+ case AS3935_NOISE_INT:
+ dev_warn(&spi->dev, "noise level is too high");
+ break;
+ }
+};
+
+static irqreturn_t as3935_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct as3935_state *st = iio_priv(indio_dev);
+
+ cancel_delayed_work(&st->work);
+ schedule_delayed_work(&st->work, jiffies_to_msecs(3));
+ return IRQ_HANDLED;
+}
+
+static void calibrate_as3935(struct as3935_state *st)
+{
+ mutex_lock(&st->lock);
+
+ /* mask disturber interrupt bit */
+ as3935_write(st, AS3935_INT, 1 << 5);
+
+ as3935_write(st, AS3935_CALIBRATE, 0x96);
+ as3935_write(st, AS3935_TUNE_CAP, 1 << 5 | st->tune_cap);
+
+ mdelay(2);
+ as3935_write(st, AS3935_TUNE_CAP, st->tune_cap);
+
+ mutex_unlock(&st->lock);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int as3935_suspend(struct spi_device *spi, pm_message_t msg)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct as3935_state *st = iio_priv(indio_dev);
+ int val, ret;
+
+ mutex_lock(&st->lock);
+ ret = as3935_read(st, AS3935_AFE_GAIN, &val);
+ if (ret)
+ return ret;
+ val |= AS3935_AFE_PWR_BIT;
+
+ ret = as3935_write(st, AS3935_AFE_GAIN, val);
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int as3935_resume(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct as3935_state *st = iio_priv(indio_dev);
+ int val, ret;
+
+ mutex_lock(&st->lock);
+ ret = as3935_read(st, AS3935_AFE_GAIN, &val);
+ if (ret)
+ return ret;
+ val &= ~AS3935_AFE_PWR_BIT;
+ ret = as3935_write(st, AS3935_AFE_GAIN, val);
+ mutex_unlock(&st->lock);
+ return ret;
+}
+#else
+#define as3935_suspend NULL
+#define as3935_resume NULL
+#endif
+
+static int as3935_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct iio_trigger *trig;
+ struct as3935_state *st;
+ struct device_node *np = spi->dev.of_node;
+ int ret;
+
+ /* Be sure lightning event interrupt */
+ if (!spi->irq) {
+ dev_err(&spi->dev, "unable to get event interrupt\n");
+ return -EINVAL;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+ st->tune_cap = 0;
+
+ spi_set_drvdata(spi, indio_dev);
+ mutex_init(&st->lock);
+ INIT_DELAYED_WORK(&st->work, as3935_event_work);
+
+ ret = of_property_read_u32(np, "ams,tune-cap", &st->tune_cap);
+ if (ret) {
+ st->tune_cap = 0;
+ dev_warn(&spi->dev,
+ "no tune-cap set, defaulting to %d", st->tune_cap);
+ }
+
+ if (st->tune_cap > 15) {
+ dev_err(&spi->dev,
+ "wrong tune-cap setting of %d\n", st->tune_cap);
+ return -EINVAL;
+ }
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->channels = as3935_channels;
+ indio_dev->num_channels = ARRAY_SIZE(as3935_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &as3935_info;
+
+ trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+
+ if (!trig)
+ return -ENOMEM;
+
+ st->trig = trig;
+ trig->dev.parent = indio_dev->dev.parent;
+ iio_trigger_set_drvdata(trig, indio_dev);
+ trig->ops = &iio_interrupt_trigger_ops;
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ dev_err(&spi->dev, "failed to register trigger\n");
+ return ret;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &as3935_trigger_handler,
+ &iio_triggered_buffer_setup_ops);
+
+ if (ret) {
+ dev_err(&spi->dev, "cannot setup iio trigger\n");
+ goto unregister_trigger;
+ }
+
+ calibrate_as3935(st);
+
+ ret = devm_request_irq(&spi->dev, spi->irq,
+ &as3935_interrupt_handler,
+ IRQF_TRIGGER_RISING,
+ dev_name(&spi->dev),
+ indio_dev);
+
+ if (ret) {
+ dev_err(&spi->dev, "unable to request irq\n");
+ goto unregister_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&spi->dev, "unable to register device\n");
+ goto unregister_trigger;
+ }
+ return 0;
+
+unregister_trigger:
+ iio_trigger_unregister(st->trig);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+};
+
+static int as3935_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct as3935_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_device_unregister(indio_dev);
+
+ return 0;
+};
+
+static const struct spi_device_id as3935_id[] = {
+ {"as3935", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(spi, as3935_id);
+
+static struct spi_driver as3935_driver = {
+ .driver = {
+ .name = "as3935",
+ .owner = THIS_MODULE,
+ },
+ .probe = as3935_probe,
+ .remove = as3935_remove,
+ .id_table = as3935_id,
+ .suspend = as3935_suspend,
+ .resume = as3935_resume,
+};
+module_spi_driver(as3935_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("AS3935 lightning sensor");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:as3935");
--
1.8.3.2
^ permalink raw reply related
* Re: [PATCH V4 5/8] SPEAr13xx: Fixup: Move SPEAr1340 SATA platform code to phy driver
From: Pratyush Anand @ 2014-02-06 7:00 UTC (permalink / raw)
To: Kishon Vijay Abraham I
Cc: arnd@arndb.de, Viresh Kumar, Tejun Heo, spear-devel,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-ide@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <52F32C8A.5060100@ti.com>
On Thu, Feb 06, 2014 at 02:32:42PM +0800, Kishon Vijay Abraham I wrote:
> Hi,
>
> On Thursday 06 February 2014 10:14 AM, Pratyush Anand wrote:
> > ahci driver needs some platform specific functions which are called at
> > init, exit, suspend and resume conditions. Till now these functions were
> > present in a platform driver with a fixme notes.
> >
> > Similar functions modifying same set of registers will also be needed in
> > case of PCIe phy init/exit.
> >
> > So move all these SATA platform code to phy-miphy40lp driver.
> >
> > Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
> > Tested-by: Mohit Kumar <mohit.kumar@st.com>
> > Cc: Viresh Kumar <viresh.linux@gmail.com>
> > Cc: Tejun Heo <tj@kernel.org>
> > Cc: Arnd Bergmann <arnd@arndb.de>
> > Cc: Kishon Vijay Abraham I <kishon@ti.com>
> > Cc: spear-devel@list.st.com
> > Cc: linux-arm-kernel@lists.infradead.org
> > Cc: devicetree@vger.kernel.org
> > Cc: linux-ide@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org
> > ---
> > .../devicetree/bindings/arm/spear-misc.txt | 4 +
> > arch/arm/boot/dts/spear1310-evb.dts | 4 +
> > arch/arm/boot/dts/spear1310.dtsi | 39 +++-
> > arch/arm/boot/dts/spear1340-evb.dts | 4 +
> > arch/arm/boot/dts/spear1340.dtsi | 13 +-
> > arch/arm/boot/dts/spear13xx.dtsi | 5 +
> > arch/arm/mach-spear/Kconfig | 2 +
> > arch/arm/mach-spear/spear1340.c | 127 +------------
> > drivers/phy/phy-miphy40lp.c | 204 ++++++++++++++++++++-
>
> It would be better if you can split this patch. Keep arch/ in separate patch
> and drivers/ in separate patch.
Code is actually moving from arch to driver. Therefore I kept it in
same patch.
> > 9 files changed, 266 insertions(+), 136 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/arm/spear-misc.txt
> >
> .
> .
> <snip>
> .
> .
> > static const char * const spear1340_dt_board_compat[] = {
> > diff --git a/drivers/phy/phy-miphy40lp.c b/drivers/phy/phy-miphy40lp.c
> > index d478c14..cc7f45d 100644
> > --- a/drivers/phy/phy-miphy40lp.c
> > +++ b/drivers/phy/phy-miphy40lp.c
> > @@ -8,6 +8,7 @@
> > * it under the terms of the GNU General Public License version 2 as
> > * published by the Free Software Foundation.
> > *
> > + * 04/02/2014: Adding support of SATA mode for SPEAr1340.
> > */
> >
> > #include <linux/delay.h>
> > @@ -19,6 +20,60 @@
> > #include <linux/phy/phy.h>
> > #include <linux/regmap.h>
> >
> > +/* SPEAr1340 Registers */
> > +/* Power Management Registers */
> > +#define SPEAR1340_PCM_CFG 0x100
> > + #define SPEAR1340_PCM_CFG_SATA_POWER_EN 0x800
> > +#define SPEAR1340_PCM_WKUP_CFG 0x104
> > +#define SPEAR1340_SWITCH_CTR 0x108
> > +
> > +#define SPEAR1340_PERIP1_SW_RST 0x318
> > + #define SPEAR1340_PERIP1_SW_RST_SATA 0x1000
> > +#define SPEAR1340_PERIP2_SW_RST 0x31C
> > +#define SPEAR1340_PERIP3_SW_RST 0x320
> > +
> > +/* PCIE - SATA configuration registers */
> > +#define SPEAR1340_PCIE_SATA_CFG 0x424
> > + /* PCIE CFG MASks */
> > + #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT (1 << 11)
>
> use BIT() wherever possible.
OK.
> > + #define SPEAR1340_PCIE_CFG_POWERUP_RESET (1 << 10)
> > + #define SPEAR1340_PCIE_CFG_CORE_CLK_EN (1 << 9)
> > + #define SPEAR1340_PCIE_CFG_AUX_CLK_EN (1 << 8)
> > + #define SPEAR1340_SATA_CFG_TX_CLK_EN (1 << 4)
> > + #define SPEAR1340_SATA_CFG_RX_CLK_EN (1 << 3)
> > + #define SPEAR1340_SATA_CFG_POWERUP_RESET (1 << 2)
> > + #define SPEAR1340_SATA_CFG_PM_CLK_EN (1 << 1)
> > + #define SPEAR1340_PCIE_SATA_SEL_PCIE (0)
> > + #define SPEAR1340_PCIE_SATA_SEL_SATA (1)
> > + #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F
> > + #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \
> > + SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
> > + SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
> > + SPEAR1340_PCIE_CFG_POWERUP_RESET | \
> > + SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
> > + #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \
> > + SPEAR1340_SATA_CFG_PM_CLK_EN | \
> > + SPEAR1340_SATA_CFG_POWERUP_RESET | \
> > + SPEAR1340_SATA_CFG_RX_CLK_EN | \
> > + SPEAR1340_SATA_CFG_TX_CLK_EN)
> > +
> > +#define SPEAR1340_PCIE_MIPHY_CFG 0x428
> > + #define SPEAR1340_MIPHY_OSC_BYPASS_EXT (1 << 31)
> > + #define SPEAR1340_MIPHY_CLK_REF_DIV2 (1 << 27)
> > + #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27)
> > + #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27)
> > + #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0)
> > + #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF
> > + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
> > + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
> > + SPEAR1340_MIPHY_CLK_REF_DIV2 | \
> > + SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
> > + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
> > + (SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
> > + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
> > + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
> > + SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
> > +
> > enum phy_mode {
> > SATA,
> > PCIE,
> > @@ -38,28 +93,145 @@ struct st_miphy40lp_priv {
> > u32 id;
> > };
> >
> > +static int spear1340_sata_miphy_init(struct st_miphy40lp_priv *phypriv)
>
> The function name format here differs from what you have already added. It will
> be good to have consistent name in the file.
You mean to pass "struct phy *phy" in all the internal functions too?
> > +{
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_SATA_CFG,
> > + SPEAR1340_PCIE_SATA_CFG_MASK, SPEAR1340_SATA_CFG_VAL);
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_MIPHY_CFG,
> > + SPEAR1340_PCIE_MIPHY_CFG_MASK,
> > + SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK);
> > + /* Switch on sata power domain */
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PCM_CFG,
> > + SPEAR1340_PCM_CFG_SATA_POWER_EN,
> > + SPEAR1340_PCM_CFG_SATA_POWER_EN);
> > + msleep(20);
> > + /* Disable PCIE SATA Controller reset */
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PERIP1_SW_RST,
> > + SPEAR1340_PERIP1_SW_RST_SATA, 0);
> > + msleep(20);
> > +
> > + return 0;
> > +}
> > +
> > +static int spear1340_sata_miphy_exit(struct st_miphy40lp_priv *phypriv)
> > +{
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_SATA_CFG,
> > + SPEAR1340_PCIE_SATA_CFG_MASK, 0);
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_MIPHY_CFG,
> > + SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
> > +
> > + /* Enable PCIE SATA Controller reset */
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PERIP1_SW_RST,
> > + SPEAR1340_PERIP1_SW_RST_SATA,
> > + SPEAR1340_PERIP1_SW_RST_SATA);
> > + msleep(20);
> > + /* Switch off sata power domain */
> > + regmap_update_bits(phypriv->misc, SPEAR1340_PCM_CFG,
> > + SPEAR1340_PCM_CFG_SATA_POWER_EN, 0);
> > + msleep(20);
> > +
> > + return 0;
> > +}
> > +
> > +static int sata_miphy_init(struct st_miphy40lp_priv *phypriv)
> > +{
> > + if (of_device_is_compatible(phypriv->np, "st,spear1340-miphy"))
>
> This compatible value is a bit confusing since it doesn't have 'sata' in it.
> spear1340 can have usb phy or pcie phy too no? How do we differentiate it then?
same spear1340 miphy is used for sata as well as for pcie. sata or
pcie mode is selected using mode args passed in phys.
> > + return spear1340_sata_miphy_init(phypriv);
> > + else
> > + return -EINVAL;
> > +}
> > +
> > +static int sata_miphy_exit(struct st_miphy40lp_priv *phypriv)
> > +{
> > + if (of_device_is_compatible(phypriv->np, "st,spear1340-miphy"))
> > + return spear1340_sata_miphy_exit(phypriv);
> > + else
> > + return -EINVAL;
> > +}
> > +
> > +static int sata_miphy_power_off(struct st_miphy40lp_priv *phypriv)
> > +{
> > + if (of_device_is_compatible(phypriv->np, "st,spear1340-miphy"))
> > + return 0;
> > + else
> > + return -EINVAL;
> > +}
> > +
> > +static int sata_miphy_power_on(struct st_miphy40lp_priv *phypriv)
> > +{
> > + if (of_device_is_compatible(phypriv->np, "st,spear1340-miphy"))
> > + return 0;
> > + else
> > + return -EINVAL;
> > +}
> > +
> > +static int sata_miphy_suspend(struct st_miphy40lp_priv *phypriv)
> > +{
> > + if (of_device_is_compatible(phypriv->np, "st,spear1340-miphy"))
> > + return spear1340_sata_miphy_exit(phypriv);
> > + else
> > + return -EINVAL;
> > +}
> > +
> > +static int sata_miphy_resume(struct st_miphy40lp_priv *phypriv)
> > +{
> > + if (of_device_is_compatible(phypriv->np, "st,spear1340-miphy"))
> > + return spear1340_sata_miphy_init(phypriv);
> > + else
> > + return -EINVAL;
> > +}
> > +
> > static int miphy40lp_init(struct phy *phy)
> > {
> > - return -EINVAL;
> > + struct st_miphy40lp_priv *phypriv = phy_get_drvdata(phy);
> > +
> > + switch (phypriv->mode) {
> > + case SATA:
> > + return sata_miphy_init(phypriv);
> > + default:
> > + return -EINVAL;
> > + }
> > }
> >
> > static int miphy40lp_exit(struct phy *phy)
> > {
> > - return -EINVAL;
> > + struct st_miphy40lp_priv *phypriv = phy_get_drvdata(phy);
> > +
> > + switch (phypriv->mode) {
> > + case SATA:
> > + return sata_miphy_exit(phypriv);
> > + default:
> > + return -EINVAL;
> > + }
> > }
> >
> > static int miphy40lp_power_off(struct phy *phy)
> > {
> > - return -EINVAL;
> > + struct st_miphy40lp_priv *phypriv = phy_get_drvdata(phy);
> > +
> > + switch (phypriv->mode) {
> > + case SATA:
> > + return sata_miphy_power_off(phypriv);
> > + default:
> > + return -EINVAL;
> > + }
> > }
> >
> > static int miphy40lp_power_on(struct phy *phy)
> > {
> > - return -EINVAL;
> > + struct st_miphy40lp_priv *phypriv = phy_get_drvdata(phy);
> > +
> > + switch (phypriv->mode) {
> > + case SATA:
> > + return sata_miphy_power_on(phypriv);
> > + default:
> > + return -EINVAL;
> > + }
> > }
> >
> > static const struct of_device_id st_miphy40lp_of_match[] = {
> > { .compatible = "st,miphy40lp-phy" },
> > + { .compatible = "st,spear1340-miphy" },
> > { },
> > };
> > MODULE_DEVICE_TABLE(of, st_miphy40lp_of_match);
> > @@ -75,12 +247,32 @@ static struct phy_ops st_miphy40lp_ops = {
> > #ifdef CONFIG_PM_SLEEP
> > static int miphy40lp_suspend(struct device *dev)
> > {
> > - return -EINVAL;
> > + struct st_miphy40lp_priv *phypriv = dev_get_drvdata(dev);
> > +
> > + if (dev->power.power_state.event == PM_EVENT_FREEZE)
> > + return 0;
>
> I'm not sure if you should be accessing it from the drivers. Will be good to
> check with PM guys.
+ linux-pm mailing list.
Rgds
Pratyush
> > +
> > + switch (phypriv->mode) {
> > + case SATA:
> > + return sata_miphy_suspend(phypriv);
> > + default:
> > + return -EINVAL;
> > + }
> > }
> >
> > static int miphy40lp_resume(struct device *dev)
> > {
> > - return -EINVAL;
> > + struct st_miphy40lp_priv *phypriv = dev_get_drvdata(dev);
> > +
> > + if (dev->power.power_state.event == PM_EVENT_THAW)
> > + return 0;
>
> Same here.
>
> Thanks
> Kishon
^ permalink raw reply
* Re: [PATCH V4 5/8] SPEAr13xx: Fixup: Move SPEAr1340 SATA platform code to phy driver
From: Kishon Vijay Abraham I @ 2014-02-06 8:01 UTC (permalink / raw)
To: Pratyush Anand
Cc: devicetree@vger.kernel.org, arnd@arndb.de, spear-devel,
linux-kernel@vger.kernel.org, linux-ide@vger.kernel.org,
Viresh Kumar, Tejun Heo, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20140206070053.GC2394@pratyush-vbox>
Hi,
On Thursday 06 February 2014 12:30 PM, Pratyush Anand wrote:
> On Thu, Feb 06, 2014 at 02:32:42PM +0800, Kishon Vijay Abraham I wrote:
>> Hi,
>>
>> On Thursday 06 February 2014 10:14 AM, Pratyush Anand wrote:
>>> ahci driver needs some platform specific functions which are called at
>>> init, exit, suspend and resume conditions. Till now these functions were
>>> present in a platform driver with a fixme notes.
>>>
>>> Similar functions modifying same set of registers will also be needed in
>>> case of PCIe phy init/exit.
>>>
>>> So move all these SATA platform code to phy-miphy40lp driver.
>>>
>>> Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
>>> Tested-by: Mohit Kumar <mohit.kumar@st.com>
>>> Cc: Viresh Kumar <viresh.linux@gmail.com>
>>> Cc: Tejun Heo <tj@kernel.org>
>>> Cc: Arnd Bergmann <arnd@arndb.de>
>>> Cc: Kishon Vijay Abraham I <kishon@ti.com>
>>> Cc: spear-devel@list.st.com
>>> Cc: linux-arm-kernel@lists.infradead.org
>>> Cc: devicetree@vger.kernel.org
>>> Cc: linux-ide@vger.kernel.org
>>> Cc: linux-kernel@vger.kernel.org
>>> ---
>>> .../devicetree/bindings/arm/spear-misc.txt | 4 +
>>> arch/arm/boot/dts/spear1310-evb.dts | 4 +
>>> arch/arm/boot/dts/spear1310.dtsi | 39 +++-
>>> arch/arm/boot/dts/spear1340-evb.dts | 4 +
>>> arch/arm/boot/dts/spear1340.dtsi | 13 +-
>>> arch/arm/boot/dts/spear13xx.dtsi | 5 +
>>> arch/arm/mach-spear/Kconfig | 2 +
>>> arch/arm/mach-spear/spear1340.c | 127 +------------
>>> drivers/phy/phy-miphy40lp.c | 204 ++++++++++++++++++++-
>>
>> It would be better if you can split this patch. Keep arch/ in separate patch
>> and drivers/ in separate patch.
>
> Code is actually moving from arch to driver. Therefore I kept it in
> same patch.
>
>>> 9 files changed, 266 insertions(+), 136 deletions(-)
>>> create mode 100644 Documentation/devicetree/bindings/arm/spear-misc.txt
>>>
>> .
>> .
>> <snip>
>> .
>> .
>>> static const char * const spear1340_dt_board_compat[] = {
>>> diff --git a/drivers/phy/phy-miphy40lp.c b/drivers/phy/phy-miphy40lp.c
>>> index d478c14..cc7f45d 100644
>>> --- a/drivers/phy/phy-miphy40lp.c
>>> +++ b/drivers/phy/phy-miphy40lp.c
>>> @@ -8,6 +8,7 @@
>>> * it under the terms of the GNU General Public License version 2 as
>>> * published by the Free Software Foundation.
>>> *
>>> + * 04/02/2014: Adding support of SATA mode for SPEAr1340.
>>> */
>>>
>>> #include <linux/delay.h>
>>> @@ -19,6 +20,60 @@
>>> #include <linux/phy/phy.h>
>>> #include <linux/regmap.h>
>>>
>>> +/* SPEAr1340 Registers */
>>> +/* Power Management Registers */
>>> +#define SPEAR1340_PCM_CFG 0x100
>>> + #define SPEAR1340_PCM_CFG_SATA_POWER_EN 0x800
>>> +#define SPEAR1340_PCM_WKUP_CFG 0x104
>>> +#define SPEAR1340_SWITCH_CTR 0x108
>>> +
>>> +#define SPEAR1340_PERIP1_SW_RST 0x318
>>> + #define SPEAR1340_PERIP1_SW_RST_SATA 0x1000
>>> +#define SPEAR1340_PERIP2_SW_RST 0x31C
>>> +#define SPEAR1340_PERIP3_SW_RST 0x320
>>> +
>>> +/* PCIE - SATA configuration registers */
>>> +#define SPEAR1340_PCIE_SATA_CFG 0x424
>>> + /* PCIE CFG MASks */
>>> + #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT (1 << 11)
>>
>> use BIT() wherever possible.
>
> OK.
>
>>> + #define SPEAR1340_PCIE_CFG_POWERUP_RESET (1 << 10)
>>> + #define SPEAR1340_PCIE_CFG_CORE_CLK_EN (1 << 9)
>>> + #define SPEAR1340_PCIE_CFG_AUX_CLK_EN (1 << 8)
>>> + #define SPEAR1340_SATA_CFG_TX_CLK_EN (1 << 4)
>>> + #define SPEAR1340_SATA_CFG_RX_CLK_EN (1 << 3)
>>> + #define SPEAR1340_SATA_CFG_POWERUP_RESET (1 << 2)
>>> + #define SPEAR1340_SATA_CFG_PM_CLK_EN (1 << 1)
>>> + #define SPEAR1340_PCIE_SATA_SEL_PCIE (0)
>>> + #define SPEAR1340_PCIE_SATA_SEL_SATA (1)
>>> + #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F
>>> + #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \
>>> + SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
>>> + SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
>>> + SPEAR1340_PCIE_CFG_POWERUP_RESET | \
>>> + SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
>>> + #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \
>>> + SPEAR1340_SATA_CFG_PM_CLK_EN | \
>>> + SPEAR1340_SATA_CFG_POWERUP_RESET | \
>>> + SPEAR1340_SATA_CFG_RX_CLK_EN | \
>>> + SPEAR1340_SATA_CFG_TX_CLK_EN)
>>> +
>>> +#define SPEAR1340_PCIE_MIPHY_CFG 0x428
>>> + #define SPEAR1340_MIPHY_OSC_BYPASS_EXT (1 << 31)
>>> + #define SPEAR1340_MIPHY_CLK_REF_DIV2 (1 << 27)
>>> + #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27)
>>> + #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27)
>>> + #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0)
>>> + #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF
>>> + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
>>> + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
>>> + SPEAR1340_MIPHY_CLK_REF_DIV2 | \
>>> + SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
>>> + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
>>> + (SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
>>> + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
>>> + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
>>> + SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
>>> +
>>> enum phy_mode {
>>> SATA,
>>> PCIE,
>>> @@ -38,28 +93,145 @@ struct st_miphy40lp_priv {
>>> u32 id;
>>> };
>>>
>>> +static int spear1340_sata_miphy_init(struct st_miphy40lp_priv *phypriv)
>>
>> The function name format here differs from what you have already added. It will
>> be good to have consistent name in the file.
>
> You mean to pass "struct phy *phy" in all the internal functions too?
No. I meant let all the function names begin with miphy40lp_.
>
>>> +{
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_SATA_CFG,
>>> + SPEAR1340_PCIE_SATA_CFG_MASK, SPEAR1340_SATA_CFG_VAL);
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_MIPHY_CFG,
>>> + SPEAR1340_PCIE_MIPHY_CFG_MASK,
>>> + SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK);
>>> + /* Switch on sata power domain */
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PCM_CFG,
>>> + SPEAR1340_PCM_CFG_SATA_POWER_EN,
>>> + SPEAR1340_PCM_CFG_SATA_POWER_EN);
>>> + msleep(20);
>>> + /* Disable PCIE SATA Controller reset */
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PERIP1_SW_RST,
>>> + SPEAR1340_PERIP1_SW_RST_SATA, 0);
>>> + msleep(20);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int spear1340_sata_miphy_exit(struct st_miphy40lp_priv *phypriv)
>>> +{
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_SATA_CFG,
>>> + SPEAR1340_PCIE_SATA_CFG_MASK, 0);
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_MIPHY_CFG,
>>> + SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
>>> +
>>> + /* Enable PCIE SATA Controller reset */
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PERIP1_SW_RST,
>>> + SPEAR1340_PERIP1_SW_RST_SATA,
>>> + SPEAR1340_PERIP1_SW_RST_SATA);
>>> + msleep(20);
>>> + /* Switch off sata power domain */
>>> + regmap_update_bits(phypriv->misc, SPEAR1340_PCM_CFG,
>>> + SPEAR1340_PCM_CFG_SATA_POWER_EN, 0);
>>> + msleep(20);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int sata_miphy_init(struct st_miphy40lp_priv *phypriv)
>>> +{
>>> + if (of_device_is_compatible(phypriv->np, "st,spear1340-miphy"))
>>
>> This compatible value is a bit confusing since it doesn't have 'sata' in it.
>> spear1340 can have usb phy or pcie phy too no? How do we differentiate it then?
>
> same spear1340 miphy is used for sata as well as for pcie. sata or
> pcie mode is selected using mode args passed in phys.
Alright. Got it while reading the next patch ;-)
Thanks
Kishon
^ permalink raw reply
* Re: [PATCH V4 5/8] SPEAr13xx: Fixup: Move SPEAr1340 SATA platform code to phy driver
From: Pratyush Anand @ 2014-02-06 8:07 UTC (permalink / raw)
To: Kishon Vijay Abraham I
Cc: arnd-r2nGTMty4D4@public.gmane.org, Viresh Kumar, Tejun Heo,
spear-devel,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-ide-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <52F34155.8010900-l0cyMroinI0@public.gmane.org>
On Thu, Feb 06, 2014 at 04:01:25PM +0800, Kishon Vijay Abraham I wrote:
> Hi,
>
> On Thursday 06 February 2014 12:30 PM, Pratyush Anand wrote:
> > On Thu, Feb 06, 2014 at 02:32:42PM +0800, Kishon Vijay Abraham I wrote:
> >> Hi,
> >>
> >> On Thursday 06 February 2014 10:14 AM, Pratyush Anand wrote:
> >>> ahci driver needs some platform specific functions which are called at
> >>> init, exit, suspend and resume conditions. Till now these functions were
> >>> present in a platform driver with a fixme notes.
> >>>
> >>> Similar functions modifying same set of registers will also be needed in
> >>> case of PCIe phy init/exit.
> >>>
> >>> So move all these SATA platform code to phy-miphy40lp driver.
> >>>
> >>> Signed-off-by: Pratyush Anand <pratyush.anand-qxv4g6HH51o@public.gmane.org>
> >>> Tested-by: Mohit Kumar <mohit.kumar-qxv4g6HH51o@public.gmane.org>
> >>> Cc: Viresh Kumar <viresh.linux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> >>> Cc: Tejun Heo <tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> >>> Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> >>> Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
> >>> Cc: spear-devel-nkJGhpqTU55BDgjK7y7TUQ@public.gmane.org
> >>> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> >>> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> >>> Cc: linux-ide-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> >>> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> >>> ---
> >>> .../devicetree/bindings/arm/spear-misc.txt | 4 +
> >>> arch/arm/boot/dts/spear1310-evb.dts | 4 +
> >>> arch/arm/boot/dts/spear1310.dtsi | 39 +++-
> >>> arch/arm/boot/dts/spear1340-evb.dts | 4 +
> >>> arch/arm/boot/dts/spear1340.dtsi | 13 +-
> >>> arch/arm/boot/dts/spear13xx.dtsi | 5 +
> >>> arch/arm/mach-spear/Kconfig | 2 +
> >>> arch/arm/mach-spear/spear1340.c | 127 +------------
> >>> drivers/phy/phy-miphy40lp.c | 204 ++++++++++++++++++++-
> >>
> >> It would be better if you can split this patch. Keep arch/ in separate patch
> >> and drivers/ in separate patch.
> >
> > Code is actually moving from arch to driver. Therefore I kept it in
> > same patch.
> >
> >>> 9 files changed, 266 insertions(+), 136 deletions(-)
> >>> create mode 100644 Documentation/devicetree/bindings/arm/spear-misc.txt
> >>>
> >> .
> >> .
> >> <snip>
> >> .
> >> .
> >>> static const char * const spear1340_dt_board_compat[] = {
> >>> diff --git a/drivers/phy/phy-miphy40lp.c b/drivers/phy/phy-miphy40lp.c
> >>> index d478c14..cc7f45d 100644
> >>> --- a/drivers/phy/phy-miphy40lp.c
> >>> +++ b/drivers/phy/phy-miphy40lp.c
> >>> @@ -8,6 +8,7 @@
> >>> * it under the terms of the GNU General Public License version 2 as
> >>> * published by the Free Software Foundation.
> >>> *
> >>> + * 04/02/2014: Adding support of SATA mode for SPEAr1340.
> >>> */
> >>>
> >>> #include <linux/delay.h>
> >>> @@ -19,6 +20,60 @@
> >>> #include <linux/phy/phy.h>
> >>> #include <linux/regmap.h>
> >>>
> >>> +/* SPEAr1340 Registers */
> >>> +/* Power Management Registers */
> >>> +#define SPEAR1340_PCM_CFG 0x100
> >>> + #define SPEAR1340_PCM_CFG_SATA_POWER_EN 0x800
> >>> +#define SPEAR1340_PCM_WKUP_CFG 0x104
> >>> +#define SPEAR1340_SWITCH_CTR 0x108
> >>> +
> >>> +#define SPEAR1340_PERIP1_SW_RST 0x318
> >>> + #define SPEAR1340_PERIP1_SW_RST_SATA 0x1000
> >>> +#define SPEAR1340_PERIP2_SW_RST 0x31C
> >>> +#define SPEAR1340_PERIP3_SW_RST 0x320
> >>> +
> >>> +/* PCIE - SATA configuration registers */
> >>> +#define SPEAR1340_PCIE_SATA_CFG 0x424
> >>> + /* PCIE CFG MASks */
> >>> + #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT (1 << 11)
> >>
> >> use BIT() wherever possible.
> >
> > OK.
> >
> >>> + #define SPEAR1340_PCIE_CFG_POWERUP_RESET (1 << 10)
> >>> + #define SPEAR1340_PCIE_CFG_CORE_CLK_EN (1 << 9)
> >>> + #define SPEAR1340_PCIE_CFG_AUX_CLK_EN (1 << 8)
> >>> + #define SPEAR1340_SATA_CFG_TX_CLK_EN (1 << 4)
> >>> + #define SPEAR1340_SATA_CFG_RX_CLK_EN (1 << 3)
> >>> + #define SPEAR1340_SATA_CFG_POWERUP_RESET (1 << 2)
> >>> + #define SPEAR1340_SATA_CFG_PM_CLK_EN (1 << 1)
> >>> + #define SPEAR1340_PCIE_SATA_SEL_PCIE (0)
> >>> + #define SPEAR1340_PCIE_SATA_SEL_SATA (1)
> >>> + #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F
> >>> + #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \
> >>> + SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
> >>> + SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
> >>> + SPEAR1340_PCIE_CFG_POWERUP_RESET | \
> >>> + SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
> >>> + #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \
> >>> + SPEAR1340_SATA_CFG_PM_CLK_EN | \
> >>> + SPEAR1340_SATA_CFG_POWERUP_RESET | \
> >>> + SPEAR1340_SATA_CFG_RX_CLK_EN | \
> >>> + SPEAR1340_SATA_CFG_TX_CLK_EN)
> >>> +
> >>> +#define SPEAR1340_PCIE_MIPHY_CFG 0x428
> >>> + #define SPEAR1340_MIPHY_OSC_BYPASS_EXT (1 << 31)
> >>> + #define SPEAR1340_MIPHY_CLK_REF_DIV2 (1 << 27)
> >>> + #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27)
> >>> + #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27)
> >>> + #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0)
> >>> + #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF
> >>> + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
> >>> + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
> >>> + SPEAR1340_MIPHY_CLK_REF_DIV2 | \
> >>> + SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
> >>> + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
> >>> + (SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
> >>> + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
> >>> + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
> >>> + SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
> >>> +
> >>> enum phy_mode {
> >>> SATA,
> >>> PCIE,
> >>> @@ -38,28 +93,145 @@ struct st_miphy40lp_priv {
> >>> u32 id;
> >>> };
> >>>
> >>> +static int spear1340_sata_miphy_init(struct st_miphy40lp_priv *phypriv)
> >>
> >> The function name format here differs from what you have already added. It will
> >> be good to have consistent name in the file.
> >
> > You mean to pass "struct phy *phy" in all the internal functions too?
>
> No. I meant let all the function names begin with miphy40lp_.
okkk.. miphy40lp_spear1340_sata_init looks better :)
Rgds
Pratyush
--
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] sh_eth: add device tree support
From: Geert Uytterhoeven @ 2014-02-06 8:16 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Rob Herring, Pawel Moll, Mark Rutland, Grant Likely,
devicetree@vger.kernel.org, Linux-sh list, Ian Campbell,
Kumar Gala, Nobuhiro Iwamatsu, Rob Landley,
linux-doc@vger.kernel.org
In-Reply-To: <201402060258.57025.sergei.shtylyov@cogentembedded.com>
On Thu, Feb 6, 2014 at 12:58 AM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> +- reg: offset and length of (1) the E-DMAC/feLic register block (required),
> + (2) the TSU register block (optional).
As there can be 2 regions, have you considered reg-names?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v3 3/6] misc: fuse: Add efuse driver for Tegra
From: Peter De Schrijver @ 2014-02-06 9:11 UTC (permalink / raw)
To: Stephen Warren
Cc: linux-arm-kernel@lists.infradead.org, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, Rob Landley, Thierry Reding,
Grant Likely, Rob Herring, Danny Huang, linux-doc@vger.kernel.org,
devicetree@vger.kernel.org
In-Reply-To: <52F28DE2.1040207@wwwdotorg.org>
On Wed, Feb 05, 2014 at 08:15:46PM +0100, Stephen Warren wrote:
> On 01/28/2014 04:36 PM, Peter De Schrijver wrote:
> > Implement fuse driver for Tegra20, Tegra30, Tegra114 and Tegra124.
>
> I assume most of this code is simply cut/paste from the existing code in
> arch/arm/mach-tegra/? If so, "git format-patch -C" would have been
> useful to highlight what changed when duplicating the files.
>
It also has been rewritten slightly. Also the Tegra124 speedo file is new.
> > diff --git a/Documentation/ABI/testing/sysfs-driver-tegra-fuse b/Documentation/ABI/testing/sysfs-driver-tegra-fuse
> > +What: /sys/devices/*/<our-device>/fuse
> > +Date: December 2013
> > +Contact: Peter De Schrijver <pdeschrijver@nvidia.com>
> > +Description: read-only access to the efuses on Tegra20, Tegra30, Tegra114
> > + and Tegra124 SoC's from NVIDIA. The efuses contain write once
> > + data programmed at the factory.
> > +Users: any user space application which wants to read the efuses on
> > + Tegra SoC's
>
> Surely this file should describe the format of the file, since that's
> part of the ABI too, right?
>
Part of the fuse data is ODM defined so possibly board specific.
> > diff --git a/drivers/misc/fuse/tegra/fuse-tegra20.c b/drivers/misc/fuse/tegra/fuse-tegra20.c
>
> > +static int tegra20_fuse_probe(struct platform_device *pdev)
> ...
> > + sku_info.revision = tegra_revision;
> > + tegra20_init_speedo_data(&sku_info, &pdev->dev);
> ...
> > +}
> > +
> > +static struct platform_driver tegra20_fuse_driver = {
> > + .probe = tegra20_fuse_probe,
> > + .driver = {
> > + .name = "tegra20_fuse",
> > + .owner = THIS_MODULE,
> > + .of_match_table = tegra20_fuse_of_match,
> > + }
> > +};
> > +
> > +static int __init tegra20_fuse_init(void)
> > +{
> > + return platform_driver_register(&tegra20_fuse_driver);
> > +}
> > +postcore_initcall(tegra20_fuse_init);
>
> That call to tegra20_init_speedo_data() now happens much later in boot.
> Are you sure there's nothing that relies on data it sets up between when
> tegra_fuse_init() is called (which is where it happens before this
> series), and the somewhat arbitrary later time when this driver probes?
>
Will check.
> > diff --git a/drivers/misc/fuse/tegra/fuse-tegra30.c b/drivers/misc/fuse/tegra/fuse-tegra30.c
>
> > +postcore_initcall(tegra30_fuse_init);
> > +
>
> There's a blank line at the end of the file. I thought checkpatch warned
> about this? But actually it doesn't seem to at least in -f mode.
>
> > diff --git a/drivers/misc/fuse/tegra/fuse.h b/drivers/misc/fuse/tegra/fuse.h
>
> > +struct tegra_sku_info {
> > + int sku_id;
> > + int cpu_process_id;
> > + int cpu_speedo_id;
> > + int cpu_speedo_value;
> > + int cpu_iddq_value;
> > + int core_process_id;
> > + int soc_speedo_id;
> > + int gpu_speedo_id;
> > + int gpu_process_id;
> > + int gpu_speedo_value;
> > + enum tegra_revision revision;
> > +};
>
> The only use of this appears to be to pass to tegra_fuse_create_sysfs()
> which prints out the fields. Will there be more users in the future?
> Otherwise, I'd be tempted to just print it out outside/before-calling
> tegra_fuse_create_sysfs().
>
> That said, I wonder if these values could/should be exposed in the sysfs
> file to make it easier to interpret the fuses?
>
That could be done I think...
> > diff --git a/drivers/misc/fuse/tegra/tegra114_speedo.c b/drivers/misc/fuse/tegra/tegra114_speedo.c
>
> It might be nice to make these filenames consistent with the others,
> e.g. fuse-speedo-tegraNNN.c/speedo-tegraNNN.c, or wrap them into
> fuse-tegraNNN.c?
>
I expect 1 speedo file per new SoC, but at least every SoC since Tegra30 has
used the same way of reading the fuse data. Hence I think it's better to
keep them separate.
> > diff --git a/drivers/misc/fuse/tegra/tegra30_speedo.c b/drivers/misc/fuse/tegra/tegra30_speedo.c
>
> > +#define FUSE_SPEEDO_CALIB_0 0x14
> > +#define FUSE_PACKAGE_INFO 0XFC
> > +#define FUSE_TEST_PROG_VER 0X28
>
> In arch/arm/mach-tegra/tegra30_speedo.c, those values are different:
>
> #define FUSE_SPEEDO_CALIB_0 0x114
> #define FUSE_PACKAGE_INFO 0X1FC
> #define FUSE_TEST_PROG_VER 0X128
>
> Was this change intentional? Perhaps it should be in a separate patch to
> highlight the change, if it's an intentional bug-fix?
This is intentional. The old files used the offset from the fuse IP block base
address. The new files use the offset in the fuse array.
Cheers,
Peter.
^ permalink raw reply
* Re: [PATCH v10 1/2] [media] exynos5-is: Adds DT binding documentation
From: Mark Rutland @ 2014-02-06 9:21 UTC (permalink / raw)
To: Arun Kumar K
Cc: LMML, linux-samsung-soc, devicetree@vger.kernel.org,
Sylwester Nawrocki, Shaik Ameer Basha
In-Reply-To: <CALt3h7-VBZgX-ueNa-Fer_RBiWkd-frNwrr8DsykXbvCab=h3w@mail.gmail.com>
On Mon, Feb 03, 2014 at 10:13:55AM +0000, Arun Kumar K wrote:
> Hi Mark,
Hi Arun,
>
> This patch and hence a full series of 13 patches is waiting for a long time now
> due to your missing ack on this DT binding patch.
> I have addressed your review comments given on earlier version -
> http://www.spinics.net/lists/devicetree/msg11550.html
>
> Please check this and give an ack if it is fine to be merged.
Apologies for the delay.
As far as I can tell this looks ok:
Acked-by: Mark Rutland <mark.rutland@arm.com>
>
> Regards
> Arun
>
> On Fri, Dec 13, 2013 at 10:42 AM, Arun Kumar K <arun.kk@samsung.com> wrote:
> > From: Shaik Ameer Basha <shaik.ameer@samsung.com>
> >
> > The patch adds the DT binding doc for exynos5 SoC camera
> > subsystem.
> >
> > Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
> > Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
> > ---
> > .../bindings/media/exynos5250-camera.txt | 136 ++++++++++++++++++++
> > 1 file changed, 136 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/media/exynos5250-camera.txt
> >
> > diff --git a/Documentation/devicetree/bindings/media/exynos5250-camera.txt b/Documentation/devicetree/bindings/media/exynos5250-camera.txt
> > new file mode 100644
> > index 0000000..0c36bc4
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/exynos5250-camera.txt
> > @@ -0,0 +1,136 @@
> > +Samsung EXYNOS5 SoC Camera Subsystem
> > +------------------------------------
> > +
> > +The Exynos5 SoC Camera subsystem comprises of multiple sub-devices
> > +represented by separate device tree nodes. Currently this includes: FIMC-LITE,
> > +MIPI CSIS and FIMC-IS.
> > +
> > +The sub-device nodes are referenced using phandles in the common 'camera' node
> > +which also includes common properties of the whole subsystem not really
> > +specific to any single sub-device, like common camera port pins or the common
> > +camera bus clocks.
> > +
> > +Common 'camera' node
> > +--------------------
> > +
> > +Required properties:
> > +
> > +- compatible : must be "samsung,exynos5250-fimc"
> > +- clocks : list of phandles and clock specifiers, corresponding
> > + to entries in the clock-names property
> > +- clock-names : must contain "sclk_bayer" entry
> > +- samsung,csis : list of phandles to the mipi-csis device nodes
> > +- samsung,fimc-lite : list of phandles to the fimc-lite device nodes
> > +- samsung,fimc-is : phandle to the fimc-is device node
> > +
> > +The pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt must be used
> > +to define a required pinctrl state named "default".
> > +
> > +'parallel-ports' node
> > +---------------------
> > +
> > +This node should contain child 'port' nodes specifying active parallel video
> > +input ports. It includes camera A, camera B and RGB bay inputs.
> > +'reg' property in the port nodes specifies the input type:
> > + 1 - parallel camport A
> > + 2 - parallel camport B
> > + 5 - RGB camera bay
> > +
> > +3, 4 are for MIPI CSI-2 bus and are already described in samsung-mipi-csis.txt
> > +
> > +Required properties:
> > +
> > +For describing the input type in the child nodes, the following properties
> > +have to be present in the parallel-ports node:
> > +- #address-cells: Must be 1
> > +- #size-cells: Must be 0
> > +
> > +Image sensor nodes
> > +------------------
> > +
> > +The sensor device nodes should be added to their control bus controller (e.g.
> > +I2C0) nodes and linked to a port node in the csis or the parallel-ports node,
> > +using the common video interfaces bindings, defined in video-interfaces.txt.
> > +
> > +Example:
> > +
> > + aliases {
> > + fimc-lite0 = &fimc_lite_0
> > + };
> > +
> > + /* Parallel bus IF sensor */
> > + i2c_0: i2c@13860000 {
> > + s5k6aa: sensor@3c {
> > + compatible = "samsung,s5k6aafx";
> > + reg = <0x3c>;
> > + vddio-supply = <...>;
> > +
> > + clock-frequency = <24000000>;
> > + clocks = <...>;
> > + clock-names = "mclk";
> > +
> > + port {
> > + s5k6aa_ep: endpoint {
> > + remote-endpoint = <&fimc0_ep>;
> > + bus-width = <8>;
> > + hsync-active = <0>;
> > + vsync-active = <1>;
> > + pclk-sample = <1>;
> > + };
> > + };
> > + };
> > + };
> > +
> > + /* MIPI CSI-2 bus IF sensor */
> > + s5c73m3: sensor@1a {
> > + compatible = "samsung,s5c73m3";
> > + reg = <0x1a>;
> > + vddio-supply = <...>;
> > +
> > + clock-frequency = <24000000>;
> > + clocks = <...>;
> > + clock-names = "mclk";
> > +
> > + port {
> > + s5c73m3_1: endpoint {
> > + data-lanes = <1 2 3 4>;
> > + remote-endpoint = <&csis0_ep>;
> > + };
> > + };
> > + };
> > +
> > + camera {
> > + compatible = "samsung,exynos5250-fimc";
> > + #address-cells = <1>;
> > + #size-cells = <1>;
> > + status = "okay";
> > +
> > + pinctrl-names = "default";
> > + pinctrl-0 = <&cam_port_a_clk_active>;
> > +
> > + samsung,csis = <&csis_0>, <&csis_1>;
> > + samsung,fimc-lite = <&fimc_lite_0>, <&fimc_lite_1>, <&fimc_lite_2>;
> > + samsung,fimc-is = <&fimc_is>;
> > +
> > + /* parallel camera ports */
> > + parallel-ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + /* camera A input */
> > + port@1 {
> > + reg = <1>;
> > + camport_a_ep: endpoint {
> > + remote-endpoint = <&s5k6aa_ep>;
> > + bus-width = <8>;
> > + hsync-active = <0>;
> > + vsync-active = <1>;
> > + pclk-sample = <1>;
> > + };
> > + };
> > + };
> > + };
> > +
> > +MIPI-CSIS device binding is defined in samsung-mipi-csis.txt, FIMC-LITE
> > +device binding is defined in exynos-fimc-lite.txt and FIMC-IS binding
> > +is defined in exynos5-fimc-is.txt.
> > --
> > 1.7.9.5
> >
>
^ permalink raw reply
* Re: [PATCH v2 3/3] Documentation: mfd: Add binding document for S2MPA01
From: Sachin Kamat @ 2014-02-06 9:27 UTC (permalink / raw)
To: LKML
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Mark Rutland,
Lee Jones, Sachin Kamat
In-Reply-To: <1389266554-23463-3-git-send-email-sachin.kamat-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Hi Mark,
On 9 January 2014 16:52, Sachin Kamat <sachin.kamat-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> Added initial binding documentation for S2MPA01 MFD.
>
> Signed-off-by: Sachin Kamat <sachin.kamat-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> * Re-organised as suggested by Mark Rutland.
> ---
> Documentation/devicetree/bindings/mfd/s2mpa01.txt | 86 +++++++++++++++++++++
> 1 file changed, 86 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/s2mpa01.txt
>
Gentle Ping.
--
With warm regards,
Sachin
--
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] DT: net: document Ethernet bindings in one place
From: Grant Likely @ 2014-02-06 9:43 UTC (permalink / raw)
To: Sergei Shtylyov, Florian Fainelli, Rob Herring
Cc: netdev, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, devicetree@vger.kernel.org, Rob Landley,
linux-doc@vger.kernel.org
In-Reply-To: <52F25A63.3010608@cogentembedded.com>
On Wed, 05 Feb 2014 18:36:03 +0300, Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> wrote:
> Hello.
>
> On 02/05/2014 03:08 PM, Grant Likely wrote:
>
> >>>>>>>>> I'm afraid that's too late, it has spread very far, so that
> >>>>>>>>> of_get_phy_mode() handles that property, not "phy-connection-type".
>
> >>>>>>>> Uggg, I guess this is a case of a defacto standard then if the kernel
> >>>>>>>> doesn't even support it.
>
> >>>>>>> Maybe I forgot to CC you on patch sent to Grant only, I sent a patch a
> >>>>>>> while ago for of_get_phy_mode() to look for both "phy-mode" and
> >>>>>>> "phy-connection-type" since the former has been a Linux invention, but
> >>>>>>> the latter is ePAPR specified.
>
> >>>>>> Here is a link to the actual patch in question, not sure which tree
> >>>>>> Grant applied it to though:
>
> >>>>>> http://lkml.indiana.edu/hypermail/linux/kernel/1311.2/00048.html
>
> >>>>> It's not the patch mail, it's Grant's "applied" reply, patch is mangled in
> >>>>> this reply, and I couldn't follow the thread. Here's the actual patch mail:
>
> >>>>> http://marc.info/?l=devicetree&m=138449662807254
>
> >>>> Florian, I didn't find this patch in Grant's official tree, so maybe you
> >>>> should ask him where is the patch already?
>
> >>> Sorry, I accidentally dropped it. It will be in the next merge window.
>
> >> Already saw it, thanks. Would that it was in 3.14 instead of course, so
> >> that I could use "phy-connection-type" in my binding...
>
> > Is 3.14 broken because of missing the patch? If so I'll get it merged as
> > a bug fix.
>
> No, it's not. I could have used "phy-connection-type" in my binding
> destined for 3.15 and document it as a preferred property as well.
You still can. We just need to make sure that your patch is applied on
top of the phy-connection-type patch.
g.
^ permalink raw reply
* Re: [PATCH] pci: Add support for creating a generic host_bridge from device tree
From: Liviu Dudau @ 2014-02-06 10:18 UTC (permalink / raw)
To: Tanmay Inamdar
Cc: Arnd Bergmann, devicetree@vger.kernel.org, linaro-kernel,
linux-pci, Will Deacon, LKML, Catalin Marinas, Bjorn Helgaas,
LAKML
In-Reply-To: <CACoXjckKRQfR3CbC1JpgbhOumED+OqiMh1_Y73-h6q=ByMNanw@mail.gmail.com>
On Wed, Feb 05, 2014 at 10:26:27PM +0000, Tanmay Inamdar wrote:
> Hello Liviu,
>
> I did not get the first email of this particular patch on any of
> subscribed mailing lists (don't know why), hence replying here.
Strange, it shows in the MARC and GMANE archive for linux-pci, probably
a hickup on your receiving side?
>
> +struct pci_host_bridge *
> +pci_host_bridge_of_init(struct device *parent, int busno, struct pci_ops *ops,
> + void *host_data, struct list_head *resources)
> +{
> + struct pci_bus *root_bus;
> + struct pci_host_bridge *bridge;
> +
> + /* first parse the host bridge bus ranges */
> + if (pci_host_bridge_of_get_ranges(parent->of_node, resources))
> + return NULL;
> +
> + /* then create the root bus */
> + root_bus = pci_create_root_bus(parent, busno, ops, host_data, resources);
> + if (!root_bus)
> + return NULL;
> +
> + bridge = to_pci_host_bridge(root_bus->bridge);
> +
> + return bridge;
> +}
>
> You are keeping the domain_nr inside pci_host_bridge structure. In
> above API, domain_nr is required in 'pci_find_bus' function called
> from 'pci_create_root_bus'. Since the bridge is allocated after
> creating root bus, 'pci_find_bus' always gets domain_nr as 0. This
> will cause problem for scanning multiple domains.
Good catch. I was switching between creating a pci_controller in arch/arm64 and
adding the needed bits in pci_host_bridge. After internal review I've decided to
add the domain_nr to pci_host_bridge, but forgot to update the code everywhere.
Thanks for reviewing this, will fix in v2.
Do you find porting to the new API straight forward?
Best regards,
Liviu
>
>
> On Mon, Feb 3, 2014 at 10:46 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Monday 03 February 2014 18:33:48 Liviu Dudau wrote:
> >> +/**
> >> + * pci_host_bridge_of_get_ranges - Parse PCI host bridge resources from DT
> >> + * @dev: device node of the host bridge having the range property
> >> + * @resources: list where the range of resources will be added after DT parsing
> >> + *
> >> + * This function will parse the "ranges" property of a PCI host bridge device
> >> + * node and setup the resource mapping based on its content. It is expected
> >> + * that the property conforms with the Power ePAPR document.
> >> + *
> >> + * Each architecture will then apply their filtering based on the limitations
> >> + * of each platform. One general restriction seems to be the number of IO space
> >> + * ranges, the PCI framework makes intensive use of struct resource management,
> >> + * and for IORESOURCE_IO types they can only be requested if they are contained
> >> + * within the global ioport_resource, so that should be limited to one IO space
> >> + * range.
> >
> > Actually we have quite a different set of restrictions around I/O space on ARM32
> > at the moment: Each host bridge can have its own 64KB range in an arbitrary
> > location on MMIO space, and the total must not exceed 2MB of I/O space.
> >
> >> + */
> >> +static int pci_host_bridge_of_get_ranges(struct device_node *dev,
> >> + struct list_head *resources)
> >> +{
> >> + struct resource *res;
> >> + struct of_pci_range range;
> >> + struct of_pci_range_parser parser;
> >> + int err;
> >> +
> >> + pr_info("PCI host bridge %s ranges:\n", dev->full_name);
> >> +
> >> + /* Check for ranges property */
> >> + err = of_pci_range_parser_init(&parser, dev);
> >> + if (err)
> >> + return err;
> >> +
> >> + pr_debug("Parsing ranges property...\n");
> >> + for_each_of_pci_range(&parser, &range) {
> >> + /* Read next ranges element */
> >> + pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ",
> >> + range.pci_space, range.pci_addr);
> >> + pr_debug("cpu_addr:0x%016llx size:0x%016llx\n",
> >> + range.cpu_addr, range.size);
> >> +
> >> + /* If we failed translation or got a zero-sized region
> >> + * (some FW try to feed us with non sensical zero sized regions
> >> + * such as power3 which look like some kind of attempt
> >> + * at exposing the VGA memory hole) then skip this range
> >> + */
> >> + if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
> >> + continue;
> >> +
> >> + res = kzalloc(sizeof(struct resource), GFP_KERNEL);
> >> + if (!res) {
> >> + err = -ENOMEM;
> >> + goto bridge_ranges_nomem;
> >> + }
> >> +
> >> + of_pci_range_to_resource(&range, dev, res);
> >> +
> >> + pci_add_resource_offset(resources, res,
> >> + range.cpu_addr - range.pci_addr);
> >> + }
> >
> > I believe of_pci_range_to_resource() will return the MMIO aperture for the
> > I/O space window here, which is not what you are supposed to pass into
> > pci_add_resource_offset.
> >
> >> +EXPORT_SYMBOL(pci_host_bridge_of_init);
> >
> > EXPORT_SYMBOL_GPL
> >
> >> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> >> index 6e34498..16febae 100644
> >> --- a/drivers/pci/probe.c
> >> +++ b/drivers/pci/probe.c
> >> @@ -1787,6 +1787,17 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
> >> list_for_each_entry_safe(window, n, resources, list) {
> >> list_move_tail(&window->list, &bridge->windows);
> >> res = window->res;
> >> + /*
> >> + * IO resources are stored in the kernel with a CPU start
> >> + * address of zero. Adjust the data accordingly and remember
> >> + * the offset
> >> + */
> >> + if (resource_type(res) == IORESOURCE_IO) {
> >> + bridge->io_offset = res->start;
> >> + res->end -= res->start;
> >> + window->offset -= res->start;
> >> + res->start = 0;
> >> + }
> >> offset = window->offset;
> >> if (res->flags & IORESOURCE_BUS)
> >
> > Won't this break all existing host bridges?
> >
> > Arnd
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--
====================
| I would like to |
| fix the world, |
| but they're not |
| giving me the |
\ source code! /
---------------
¯\_(ツ)_/¯
^ permalink raw reply
* [PATCH] can: xilinx CAN controller support.
From: Kedareswara rao Appana @ 2014-02-06 10:19 UTC (permalink / raw)
To: wg-5Yr1BZd7O62+XT7JhA+gdA, mkl-bIcnvbaLZ9MEGnE8C9+IrQ,
michal.simek-gjFFaj9aHVfQT0dZR+AlfA,
grant.likely-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, linux-can-u79uwXL29TY76Z2rM5mHXA
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Kedareswara rao Appana
This patch adds xilinx CAN controller support.
This driver supports both ZYNQ CANPS IP and
Soft IP AXI CAN controller.
Signed-off-by: Kedareswara rao Appana <appanad-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
---
This patch is rebased on the 3.14 rc1 kernel.
---
.../devicetree/bindings/net/can/xilinx_can.txt | 43 +
drivers/net/can/Kconfig | 8 +
drivers/net/can/Makefile | 1 +
drivers/net/can/xilinx_can.c | 1150 ++++++++++++++++++++
4 files changed, 1202 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/can/xilinx_can.txt
create mode 100644 drivers/net/can/xilinx_can.c
diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
new file mode 100644
index 0000000..34f9643
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
@@ -0,0 +1,43 @@
+Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
+---------------------------------------------------------
+
+Required properties:
+- compatible : Should be "xlnx,zynq-can-1.00.a" for Zynq CAN
+ controllers and "xlnx,axi-can-1.00.a" for Axi CAN
+ controllers.
+- reg : Physical base address and size of the Axi CAN/Zynq
+ CANPS registers map.
+- interrupts : Property with a value describing the interrupt
+ number.
+- interrupt-parent : Must be core interrupt controller
+- clock-names : List of input clock names - "ref_clk", "aper_clk"
+ (See clock bindings for details. Two clocks are
+ required for Zynq CAN. For Axi CAN
+ case it is one(ref_clk)).
+- clocks : Clock phandles (see clock bindings for details).
+- xlnx,can-tx-dpth : Can Tx fifo depth (Required for Axi CAN).
+- xlnx,can-rx-dpth : Can Rx fifo depth (Required for Axi CAN).
+
+
+Example:
+
+For Zynq CANPS Dts file:
+ zynq_can_0: zynq-can@e0008000 {
+ compatible = "xlnx,zynq-can-1.00.a";
+ clocks = <&clkc 19>, <&clkc 36>;
+ clock-names = "ref_clk", "aper_clk";
+ reg = <0xe0008000 0x1000>;
+ interrupts = <0 28 4>;
+ interrupt-parent = <&intc>;
+ };
+For Axi CAN Dts file:
+ axi_can_0: axi-can@40000000 {
+ compatible = "xlnx,axi-can-1.00.a";
+ clocks = <&clkc 0>;
+ clock-names = "ref_clk" ;
+ reg = <0x40000000 0x10000>;
+ interrupt-parent = <&intc>;
+ interrupts = <0 59 1>;
+ xlnx,can-tx-dpth = <0x40>;
+ xlnx,can-rx-dpth = <0x40>;
+ };
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index d447b88..2344fb5 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -125,6 +125,14 @@ config CAN_GRCAN
endian syntheses of the cores would need some modifications on
the hardware level to work.
+config CAN_XILINXCAN
+ tristate "Xilinx CAN"
+ depends on ARCH_ZYNQ || MICROBLAZE
+ default n
+ ---help---
+ Xilinx CAN driver. This driver supports both soft AXI CAN IP and
+ Zynq CANPS IP.
+
source "drivers/net/can/mscan/Kconfig"
source "drivers/net/can/sja1000/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index c744039..0b8e11e 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -25,5 +25,6 @@ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
obj-$(CONFIG_PCH_CAN) += pch_can.o
obj-$(CONFIG_CAN_GRCAN) += grcan.o
+obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
new file mode 100644
index 0000000..c1b2b9d
--- /dev/null
+++ b/drivers/net/can/xilinx_can.c
@@ -0,0 +1,1150 @@
+/* Xilinx CAN device driver
+ *
+ * Copyright (C) 2012 - 2014 Xilinx, Inc.
+ * Copyright (C) 2009 PetaLogix. All rights reserved.
+ *
+ * Description:
+ * This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/led.h>
+
+#define DRIVER_NAME "XILINX_CAN"
+
+/* CAN registers set */
+#define XCAN_SRR_OFFSET 0x00 /* Software reset */
+#define XCAN_MSR_OFFSET 0x04 /* Mode select */
+#define XCAN_BRPR_OFFSET 0x08 /* Baud rate prescaler */
+#define XCAN_BTR_OFFSET 0x0C /* Bit timing */
+#define XCAN_ECR_OFFSET 0x10 /* Error counter */
+#define XCAN_ESR_OFFSET 0x14 /* Error status */
+#define XCAN_SR_OFFSET 0x18 /* Status */
+#define XCAN_ISR_OFFSET 0x1C /* Interrupt status */
+#define XCAN_IER_OFFSET 0x20 /* Interrupt enable */
+#define XCAN_ICR_OFFSET 0x24 /* Interrupt clear */
+#define XCAN_TXFIFO_ID_OFFSET 0x30 /* TX FIFO ID */
+#define XCAN_TXFIFO_DLC_OFFSET 0x34 /* TX FIFO DLC */
+#define XCAN_TXFIFO_DW1_OFFSET 0x38 /* TX FIFO Data Word 1 */
+#define XCAN_TXFIFO_DW2_OFFSET 0x3C /* TX FIFO Data Word 2 */
+#define XCAN_RXFIFO_ID_OFFSET 0x50 /* RX FIFO ID */
+#define XCAN_RXFIFO_DLC_OFFSET 0x54 /* RX FIFO DLC */
+#define XCAN_RXFIFO_DW1_OFFSET 0x58 /* RX FIFO Data Word 1 */
+#define XCAN_RXFIFO_DW2_OFFSET 0x5C /* RX FIFO Data Word 2 */
+
+/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
+#define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */
+#define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */
+#define XCAN_MSR_LBACK_MASK 0x00000002 /* Loop back mode select */
+#define XCAN_MSR_SLEEP_MASK 0x00000001 /* Sleep mode select */
+#define XCAN_BRPR_BRP_MASK 0x000000FF /* Baud rate prescaler */
+#define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */
+#define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */
+#define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */
+#define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */
+#define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */
+#define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */
+#define XCAN_ESR_BERR_MASK 0x00000008 /* Bit error */
+#define XCAN_ESR_STER_MASK 0x00000004 /* Stuff error */
+#define XCAN_ESR_FMER_MASK 0x00000002 /* Form error */
+#define XCAN_ESR_CRCER_MASK 0x00000001 /* CRC error */
+#define XCAN_SR_TXFLL_MASK 0x00000400 /* TX FIFO is full */
+#define XCAN_SR_ESTAT_MASK 0x00000180 /* Error status */
+#define XCAN_SR_ERRWRN_MASK 0x00000040 /* Error warning */
+#define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */
+#define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */
+#define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */
+#define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */
+#define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */
+#define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */
+#define XCAN_IXR_BSOFF_MASK 0x00000200 /* Bus off interrupt */
+#define XCAN_IXR_ERROR_MASK 0x00000100 /* Error interrupt */
+#define XCAN_IXR_RXNEMP_MASK 0x00000080 /* RX FIFO NotEmpty intr */
+#define XCAN_IXR_RXOFLW_MASK 0x00000040 /* RX FIFO Overflow intr */
+#define XCAN_IXR_RXOK_MASK 0x00000010 /* Message received intr */
+#define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */
+#define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */
+#define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */
+#define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */
+#define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */
+#define XCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */
+#define XCAN_IDR_RTR_MASK 0x00000001 /* Remote TX request */
+#define XCAN_DLCR_DLC_MASK 0xF0000000 /* Data length code */
+
+#define XCAN_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
+ XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
+ XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
+ XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK)
+
+/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
+#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */
+#define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */
+#define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */
+#define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */
+#define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */
+#define XCAN_ESR_REC_SHIFT 8 /* Rx Error Count */
+
+/* CAN frame length constants */
+#define XCAN_ECHO_SKB_MAX 64
+#define XCAN_NAPI_WEIGHT 64
+#define XCAN_FRAME_MAX_DATA_LEN 8
+#define XCAN_TIMEOUT (50 * HZ)
+
+/**
+ * struct xcan_priv - This definition define CAN driver instance
+ * @can: CAN private data structure.
+ * @open_time: For holding timeout values
+ * @waiting_ech_skb_index: Pointer for skb
+ * @ech_skb_next: This tell the next packet in the queue
+ * @waiting_ech_skb_num: Gives the number of packets waiting
+ * @xcan_echo_skb_max_tx: Maximum number packets the driver can send
+ * @xcan_echo_skb_max_rx: Maximum number packets the driver can receive
+ * @napi: NAPI structure
+ * @ech_skb_lock: For spinlock purpose
+ * @read_reg: For reading data from CAN registers
+ * @write_reg: For writing data to CAN registers
+ * @dev: Network device data structure
+ * @reg_base: Ioremapped address to registers
+ * @irq_flags: For request_irq()
+ * @aperclk: Pointer to struct clk
+ * @devclk: Pointer to struct clk
+ */
+struct xcan_priv {
+ struct can_priv can;
+ int open_time;
+ int waiting_ech_skb_index;
+ int ech_skb_next;
+ int waiting_ech_skb_num;
+ int xcan_echo_skb_max_tx;
+ int xcan_echo_skb_max_rx;
+ struct napi_struct napi;
+ spinlock_t ech_skb_lock;
+ u32 (*read_reg)(const struct xcan_priv *priv, int reg);
+ void (*write_reg)(const struct xcan_priv *priv, int reg, u32 val);
+ struct net_device *dev;
+ void __iomem *reg_base;
+ unsigned long irq_flags;
+ struct clk *aperclk;
+ struct clk *devclk;
+};
+
+/* CAN Bittiming constants as per Xilinx CAN specs */
+static struct can_bittiming_const xcan_bittiming_const = {
+ .name = DRIVER_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 256,
+ .brp_inc = 1,
+};
+
+/**
+ * xcan_write_reg - Write a value to the device register
+ * @priv: Driver private data structure
+ * @reg: Register offset
+ * @val: Value to write at the Register offset
+ *
+ * Write data to the paricular CAN register
+ */
+static void xcan_write_reg(const struct xcan_priv *priv, int reg, u32 val)
+{
+ writel(val, priv->reg_base + reg);
+}
+
+/**
+ * xcan_read_reg - Read a value from the device register
+ * @priv: Driver private data structure
+ * @reg: Register offset
+ *
+ * Read data from the particular CAN register
+ * Return: value read from the CAN register
+ */
+static u32 xcan_read_reg(const struct xcan_priv *priv, int reg)
+{
+ return readl(priv->reg_base + reg);
+}
+
+/**
+ * set_reset_mode - Resets the CAN device mode
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the driver reset mode routine.The driver
+ * enters into configuration mode.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int set_reset_mode(struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ unsigned long timeout;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_OFFSET);
+
+ timeout = jiffies + XCAN_TIMEOUT;
+ while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) {
+ if (time_after(jiffies, timeout)) {
+ netdev_warn(ndev, "timedout waiting for config mode\n");
+ return -ETIMEDOUT;
+ }
+ schedule_timeout(1);
+ }
+
+ return 0;
+}
+
+/**
+ * xcan_set_bittiming - CAN set bit timing routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the driver set bittiming routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_set_bittiming(struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u32 btr0, btr1;
+ u32 is_config_mode;
+
+ /* Check whether Xilinx CAN is in configuration mode.
+ * It cannot set bit timing if Xilinx CAN is not in configuration mode.
+ */
+ is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) &
+ XCAN_SR_CONFIG_MASK;
+ if (!is_config_mode) {
+ netdev_alert(ndev,
+ "Cannot set bittiming can is not in config mode\n");
+ return -EPERM;
+ }
+
+ netdev_dbg(ndev, "brp=%d,prop=%d,phase_seg1:%d,phase_reg2=%d,sjw=%d\n",
+ bt->brp, bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
+ bt->sjw);
+
+ /* Setting Baud Rate prescalar value in BRPR Register */
+ btr0 = (bt->brp - 1) & XCAN_BRPR_BRP_MASK;
+
+ /* Setting Time Segment 1 in BTR Register */
+ btr1 = (bt->prop_seg + bt->phase_seg1 - 1) & XCAN_BTR_TS1_MASK;
+
+ /* Setting Time Segment 2 in BTR Register */
+ btr1 |= ((bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT) &
+ XCAN_BTR_TS2_MASK;
+
+ /* Setting Synchronous jump width in BTR Register */
+ btr1 |= ((bt->sjw - 1) << XCAN_BTR_SJW_SHIFT) & XCAN_BTR_SJW_MASK;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ netdev_info(ndev, "Doesn't support Triple Sampling\n");
+ netdev_dbg(ndev, "Setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
+ priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
+
+ netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
+ priv->read_reg(priv, XCAN_BRPR_OFFSET),
+ priv->read_reg(priv, XCAN_BTR_OFFSET));
+
+ return 0;
+}
+
+/**
+ * xcan_start - This the drivers start routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the drivers start routine.
+ * Based on the State of the CAN device it puts
+ * the CAN device into a proper mode.
+ *
+ * Return: 0 always
+ */
+static int xcan_start(struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+
+ /* Check if it is in reset mode */
+ if (priv->can.state != CAN_STATE_STOPPED)
+ set_reset_mode(ndev);
+
+ /* Enable interrupts */
+ priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
+
+ /* Check whether it is loopback mode or normal mode */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ /* Put device into loopback mode */
+ priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_LBACK_MASK);
+ else
+ /* The device is in normal mode */
+ priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
+
+ if (priv->can.state == CAN_STATE_STOPPED) {
+ /* Enable Xilinx CAN */
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ while ((priv->read_reg(priv, XCAN_SR_OFFSET) &
+ XCAN_SR_LBACK_MASK) == 0)
+ ;
+ } else {
+ while ((priv->read_reg(priv, XCAN_SR_OFFSET)
+ & XCAN_SR_NORMAL_MASK) == 0)
+ ;
+ }
+ netdev_dbg(ndev, "status:#x%08x\n",
+ priv->read_reg(priv, XCAN_SR_OFFSET));
+ }
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ return 0;
+}
+
+/**
+ * xcan_do_set_mode - This sets the mode of the driver
+ * @ndev: Pointer to net_device structure
+ * @mode: Tells the mode of the driver
+ *
+ * This check the drivers state and calls the
+ * the corresponding modes to set.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ netdev_dbg(ndev, "Setting the mode of the driver%s\n", __func__);
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ ret = xcan_start(ndev);
+ if (ret < 0)
+ netdev_err(ndev, "xcan_start failed!\n");
+
+ if (netif_queue_stopped(ndev))
+ netif_wake_queue(ndev);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * xcan_start_xmit - Starts the transmission
+ * @skb: sk_buff pointer that contains data to be Txed
+ * @ndev: Pointer to net_device structure
+ *
+ * This function is invoked from upper layers to initiate transmission. This
+ * function uses the next available free txbuff and populates their fields to
+ * start the transmission.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ u32 id, dlc, tmp_dw1, tmp_dw2 = 0, data1, data2 = 0;
+ unsigned long flags;
+
+ /* Check if the TX buffer is full */
+ if (priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_TXFLL_MASK) {
+ netif_stop_queue(ndev);
+ netdev_err(ndev, "TX register is still full!\n");
+ return NETDEV_TX_BUSY;
+ } else if (priv->waiting_ech_skb_num == priv->xcan_echo_skb_max_tx) {
+ netif_stop_queue(ndev);
+ netdev_err(ndev, "waiting:0x%08x, max:0x%08x\n",
+ priv->waiting_ech_skb_num, priv->xcan_echo_skb_max_tx);
+ return NETDEV_TX_BUSY;
+ }
+ /* Watch carefully on the bit sequence */
+ if ((cf->can_id & CAN_EFF_FLAG) == 0) {
+ /* Standard CAN ID format */
+ id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
+ XCAN_IDR_ID1_MASK;
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ /* Extended frames remote TX request */
+ id |= XCAN_IDR_SRR_MASK;
+ } else {
+ /* Extended CAN ID format */
+ id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
+ XCAN_IDR_ID2_MASK;
+ id |= (((cf->can_id & CAN_EFF_MASK) >>
+ (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
+ XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
+
+ /* The substibute remote TX request bit should be "1"
+ * for extended frames as in the Xilinx CAN datasheet
+ */
+ id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ /* Extended frames remote TX request */
+ id |= XCAN_IDR_RTR_MASK;
+ }
+
+ dlc = (cf->can_dlc & 0xf) << XCAN_DLCR_DLC_SHIFT;
+
+ tmp_dw1 = le32_to_cpup((u32 *)(cf->data));
+ data1 = htonl(tmp_dw1);
+ if (dlc > 4) {
+ tmp_dw2 = le32_to_cpup((u32 *)(cf->data + 4));
+ data2 = htonl(tmp_dw2);
+ }
+
+ netdev_dbg(ndev, "tx:id=0x%08x,dlc=0x%08x,d1=0x%08x,d2=0x%08x\n",
+ id, dlc, data1, data2);
+
+ /* Write the Frame to Xilinx CAN TX FIFO */
+ priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id);
+ priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc);
+ priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data1);
+ priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data2);
+ stats->tx_bytes += cf->can_dlc;
+ ndev->trans_start = jiffies;
+
+ can_put_echo_skb(skb, ndev, priv->ech_skb_next);
+
+ priv->ech_skb_next = (priv->ech_skb_next + 1) %
+ priv->xcan_echo_skb_max_tx;
+
+ spin_lock_irqsave(&priv->ech_skb_lock, flags);
+ priv->waiting_ech_skb_num++;
+ spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * xcan_rx - Is called from CAN isr to complete the received
+ * frame processing
+ * @ndev: Pointer to net_device structure
+ *
+ * This function is invoked from the CAN isr(poll) to process the Rx frames. It
+ * does minimal processing and invokes "netif_receive_skb" to complete further
+ * processing.
+ * Return: 0 on success and negative error value on error
+ */
+static int xcan_rx(struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u32 id_xcan, dlc, data1, data2;
+
+ skb = alloc_can_skb(ndev, &cf);
+ if (!skb)
+ return -ENOMEM;
+
+ /* Read a frame from Xilinx zynq CANPS */
+ id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET);
+ dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) & XCAN_DLCR_DLC_MASK;
+ data1 = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
+ data2 = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
+ netdev_dbg(ndev, "rx:id=0x%08x,dlc=0x%08x,d1=0x%08x,d2=0x%08x\n",
+ id_xcan, dlc, data1, data2);
+
+ /* Change Xilinx CAN data length format to socketCAN data format */
+ cf->can_dlc = get_can_dlc((dlc & XCAN_DLCR_DLC_MASK) >>
+ XCAN_DLCR_DLC_SHIFT);
+
+ /* Change Xilinx CAN ID format to socketCAN ID format */
+ if (id_xcan & XCAN_IDR_IDE_MASK) {
+ /* The received frame is an Extended format frame */
+ cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
+ cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
+ XCAN_IDR_ID2_SHIFT;
+ cf->can_id |= CAN_EFF_FLAG;
+ if (id_xcan & XCAN_IDR_RTR_MASK)
+ cf->can_id |= CAN_RTR_FLAG;
+ } else {
+ /* The received frame is a standard format frame */
+ cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
+ XCAN_IDR_ID1_SHIFT;
+ if (id_xcan & XCAN_IDR_RTR_MASK)
+ cf->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Change Xilinx CAN data format to socketCAN data format */
+ *(u32 *)(cf->data) = ntohl(data1);
+ if (cf->can_dlc > 4)
+ *(u32 *)(cf->data + 4) = ntohl(data2);
+ else
+ *(u32 *)(cf->data + 4) = 0;
+ stats->rx_bytes += cf->can_dlc;
+
+ can_led_event(ndev, CAN_LED_EVENT_RX);
+
+ netif_receive_skb(skb);
+
+ stats->rx_packets++;
+ return 0;
+}
+
+/**
+ * xcan_err_interrupt - error frame Isr
+ * @ndev: net_device pointer
+ * @isr: interrupt status register value
+ *
+ * This is the CAN error interrupt and it will
+ * check the the type of error and forward the error
+ * frame to upper layers.
+ */
+static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u32 err_status, status;
+
+ skb = alloc_can_err_skb(ndev, &cf);
+ if (!skb) {
+ netdev_err(ndev, "alloc_can_err_skb() failed!\n");
+ return;
+ }
+
+ err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
+ priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
+ status = priv->read_reg(priv, XCAN_SR_OFFSET);
+
+ if (isr & XCAN_IXR_BSOFF_MASK) {
+ priv->can.state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ priv->can.can_stats.bus_off++;
+ /* Leave device in Config Mode in bus-off state */
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+ can_bus_off(ndev);
+ } else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) {
+ cf->can_id |= CAN_ERR_CRTL;
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ priv->can.can_stats.error_passive++;
+ cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE |
+ CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (status & XCAN_SR_ERRWRN_MASK) {
+ cf->can_id |= CAN_ERR_CRTL;
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ priv->can.can_stats.error_warning++;
+ cf->data[1] |= CAN_ERR_CRTL_RX_WARNING |
+ CAN_ERR_CRTL_TX_WARNING;
+ }
+
+ /* Check for Arbitration lost interrupt */
+ if (isr & XCAN_IXR_ARBLST_MASK) {
+ cf->can_id |= CAN_ERR_LOSTARB;
+ cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
+ priv->can.can_stats.arbitration_lost++;
+ }
+
+ /* Check for RX FIFO Overflow interrupt */
+ if (isr & XCAN_IXR_RXOFLW_MASK) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+ }
+
+ /* Check for error interrupt */
+ if (isr & XCAN_IXR_ERROR_MASK) {
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+
+ /* Check for Ack error interrupt */
+ if (err_status & XCAN_ESR_ACKER_MASK) {
+ cf->can_id |= CAN_ERR_ACK;
+ cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+ stats->tx_errors++;
+ }
+
+ /* Check for Bit error interrupt */
+ if (err_status & XCAN_ESR_BERR_MASK) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_BIT;
+ stats->tx_errors++;
+ }
+
+ /* Check for Stuff error interrupt */
+ if (err_status & XCAN_ESR_STER_MASK) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_STUFF;
+ stats->rx_errors++;
+ }
+
+ /* Check for Form error interrupt */
+ if (err_status & XCAN_ESR_FMER_MASK) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_FORM;
+ stats->rx_errors++;
+ }
+
+ /* Check for CRC error interrupt */
+ if (err_status & XCAN_ESR_CRCER_MASK) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL;
+ stats->rx_errors++;
+ }
+ priv->can.can_stats.bus_error++;
+ }
+
+ netif_rx(skb);
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ netdev_dbg(ndev, "%s: error status register:0x%x\n",
+ __func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
+}
+
+/**
+ * xcan_state_interrupt - It will check the state of the CAN device
+ * @ndev: net_device pointer
+ * @isr: interrupt status register value
+ *
+ * This will checks the state of the CAN device
+ * and puts the device into appropriate state.
+ */
+static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+
+ /* Check for Sleep interrupt if set put CAN device in sleep state */
+ if (isr & XCAN_IXR_SLP_MASK)
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ /* Check for Wake up interrupt if set put CAN device in Active state */
+ if (isr & XCAN_IXR_WKUP_MASK)
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+/**
+ * xcan_rx_poll - Poll routine for rx packets (NAPI)
+ * @napi: napi structure pointer
+ * @quota: Max number of rx packets to be processed.
+ *
+ * This is the poll routine for rx part.
+ * It will process the packets maximux quota value.
+ *
+ * Return: number of packets received
+ */
+static int xcan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *ndev = napi->dev;
+ struct xcan_priv *priv = netdev_priv(ndev);
+ u32 isr, ier;
+ int work_done = 0;
+
+ isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+ while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
+ if (isr & XCAN_IXR_RXOK_MASK) {
+ priv->write_reg(priv, XCAN_ICR_OFFSET,
+ XCAN_IXR_RXOK_MASK);
+ if (xcan_rx(ndev) < 0)
+ return work_done;
+ work_done++;
+ } else {
+ priv->write_reg(priv, XCAN_ICR_OFFSET,
+ XCAN_IXR_RXNEMP_MASK);
+ break;
+ }
+ priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
+ isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+ }
+
+ if (work_done < quota) {
+ napi_complete(napi);
+ ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+ ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK);
+ priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+ }
+ return work_done;
+}
+
+/**
+ * xcan_tx_interrupt - Tx Done Isr
+ * @ndev: net_device pointer
+ */
+static void xcan_tx_interrupt(struct net_device *ndev)
+{
+ unsigned long flags;
+ struct xcan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ u32 processed = 0, txpackets;
+
+ stats->tx_packets++;
+ netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
+ priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
+
+ txpackets = priv->waiting_ech_skb_num;
+
+ if (txpackets) {
+ can_get_echo_skb(ndev, priv->waiting_ech_skb_index);
+ priv->waiting_ech_skb_index =
+ (priv->waiting_ech_skb_index + 1) %
+ priv->xcan_echo_skb_max_tx;
+ processed++;
+ txpackets--;
+ }
+
+ spin_lock_irqsave(&priv->ech_skb_lock, flags);
+ priv->waiting_ech_skb_num -= processed;
+ spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
+
+ netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
+ priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
+
+ netif_wake_queue(ndev);
+
+ can_led_event(ndev, CAN_LED_EVENT_TX);
+}
+
+/**
+ * xcan_interrupt - CAN Isr
+ * @irq: irq number
+ * @dev_id: device id poniter
+ *
+ * This is the xilinx CAN Isr. It checks for the type of interrupt
+ * and invokes the corresponding ISR.
+ *
+ * Return:
+ * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
+ */
+static irqreturn_t xcan_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct xcan_priv *priv = netdev_priv(ndev);
+ u32 isr, ier;
+
+ if (priv->can.state == CAN_STATE_STOPPED)
+ return IRQ_NONE;
+
+ /* Get the interrupt status from Xilinx CAN */
+ isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+ if (!isr)
+ return IRQ_NONE;
+
+ netdev_dbg(ndev, "%s: isr:#x%08x, err:#x%08x\n", __func__,
+ isr, priv->read_reg(priv, XCAN_ESR_OFFSET));
+
+ /* Check for the type of interrupt and Processing it */
+ if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) {
+ priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK |
+ XCAN_IXR_WKUP_MASK));
+ xcan_state_interrupt(ndev, isr);
+ }
+
+ /* Check for Tx interrupt and Processing it */
+ if (isr & XCAN_IXR_TXOK_MASK) {
+ priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
+ xcan_tx_interrupt(ndev);
+ }
+
+ /* Check for the type of error interrupt and Processing it */
+ if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
+ XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) {
+ priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK |
+ XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK |
+ XCAN_IXR_ARBLST_MASK));
+ xcan_err_interrupt(ndev, isr);
+ }
+
+ /* Check for the type of receive interrupt and Processing it */
+ if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) {
+ ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+ ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK);
+ priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+ napi_schedule(&priv->napi);
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * xcan_stop - Driver stop routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the drivers stop routine. It will disable the
+ * interrupts and put the device into configuration mode.
+ */
+static void xcan_stop(struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ u32 ier;
+
+ /* Disable interrupts and leave the can in configuration mode */
+ ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+ ier &= ~XCAN_INTR_ALL;
+ priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+ priv->can.state = CAN_STATE_STOPPED;
+}
+
+/**
+ * xcan_open - Driver open routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the driver open routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_open(struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+ int err;
+
+ /* Set chip into reset mode */
+ err = set_reset_mode(ndev);
+ if (err < 0)
+ netdev_err(ndev, "mode resetting failed failed!\n");
+
+ /* Common open */
+ err = open_candev(ndev);
+ if (err)
+ return err;
+
+ err = xcan_start(ndev);
+ if (err < 0)
+ netdev_err(ndev, "xcan_start failed!\n");
+
+
+ can_led_event(ndev, CAN_LED_EVENT_OPEN);
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+/**
+ * xcan_close - Driver close routine
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 always
+ */
+static int xcan_close(struct net_device *ndev)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ xcan_stop(ndev);
+ close_candev(ndev);
+
+ can_led_event(ndev, CAN_LED_EVENT_STOP);
+
+ return 0;
+}
+
+/**
+ * xcan_get_berr_counter - error counter routine
+ * @ndev: Pointer to net_device structure
+ * @bec: Pointer to can_berr_counter structure
+ *
+ * This is the driver error counter routine.
+ * Return: 0 always
+ */
+static int xcan_get_berr_counter(const struct net_device *ndev,
+ struct can_berr_counter *bec)
+{
+ struct xcan_priv *priv = netdev_priv(ndev);
+
+ bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
+ bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
+ XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
+ return 0;
+}
+
+static const struct net_device_ops xcan_netdev_ops = {
+ .ndo_open = xcan_open,
+ .ndo_stop = xcan_close,
+ .ndo_start_xmit = xcan_start_xmit,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * xcan_suspend - Suspend method for the driver
+ * @_dev: Address of the platform_device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int xcan_suspend(struct device *_dev)
+{
+ struct platform_device *pdev = container_of(_dev,
+ struct platform_device, dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct xcan_priv *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ netif_stop_queue(ndev);
+ netif_device_detach(ndev);
+ }
+
+ priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ clk_disable(priv->aperclk);
+ clk_disable(priv->devclk);
+
+ return 0;
+}
+
+/**
+ * xcan_resume - Resume from suspend
+ * @dev: Address of the platformdevice structure
+ *
+ * Resume operation after suspend.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_resume(struct device *dev)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct xcan_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ ret = clk_enable(priv->aperclk);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ return ret;
+ }
+ ret = clk_enable(priv->devclk);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ return ret;
+ }
+
+ priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ if (netif_running(ndev)) {
+ netif_device_attach(ndev);
+ netif_start_queue(ndev);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
+
+/**
+ * xcan_probe - Platform registration call
+ * @pdev: Handle to the platform device structure
+ *
+ * This function does all the memory allocation and registration for the CAN
+ * device.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_probe(struct platform_device *pdev)
+{
+ struct resource *res; /* IO mem resources */
+ struct net_device *ndev;
+ struct xcan_priv *priv;
+ int ret, fifodep;
+
+ /* Create a CAN device instance */
+ ndev = alloc_candev(sizeof(struct xcan_priv), XCAN_ECHO_SKB_MAX);
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+ priv->dev = ndev;
+ priv->can.bittiming_const = &xcan_bittiming_const;
+ priv->can.do_set_bittiming = xcan_set_bittiming;
+ priv->can.do_set_mode = xcan_do_set_mode;
+ priv->can.do_get_berr_counter = xcan_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+ CAN_CTRLMODE_BERR_REPORTING;
+ priv->xcan_echo_skb_max_tx = XCAN_ECHO_SKB_MAX;
+ priv->xcan_echo_skb_max_rx = XCAN_NAPI_WEIGHT;
+
+ /* Get IRQ for the device */
+ ndev->irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(&pdev->dev, ndev->irq, &xcan_interrupt,
+ priv->irq_flags, dev_name(&pdev->dev),
+ (void *)ndev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Irq allocation for CAN failed\n");
+ goto err_free;
+ }
+
+ spin_lock_init(&priv->ech_skb_lock);
+ ndev->flags |= IFF_ECHO; /* We support local echo */
+
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ ndev->netdev_ops = &xcan_netdev_ops;
+
+ /* Get the virtual base address for the device */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->reg_base)) {
+ ret = PTR_ERR(priv->reg_base);
+ goto err_free;
+ }
+ ndev->mem_start = res->start;
+ ndev->mem_end = res->end;
+
+ priv->write_reg = xcan_write_reg;
+ priv->read_reg = xcan_read_reg;
+
+ /* Getting the CAN devclk info */
+ priv->devclk = devm_clk_get(&pdev->dev, "ref_clk");
+ if (IS_ERR(priv->devclk)) {
+ dev_err(&pdev->dev, "Device clock not found.\n");
+ ret = PTR_ERR(priv->devclk);
+ goto err_free;
+ }
+
+ /* Check for type of CAN device */
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "xlnx,zynq-can-1.00.a")) {
+ priv->aperclk = devm_clk_get(&pdev->dev, "aper_clk");
+ if (IS_ERR(priv->aperclk)) {
+ dev_err(&pdev->dev, "aper clock not found\n");
+ ret = PTR_ERR(priv->aperclk);
+ goto err_free;
+ }
+ } else {
+ priv->aperclk = priv->devclk;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "xlnx,can-tx-dpth", &fifodep);
+ if (ret < 0)
+ goto err_free;
+ priv->xcan_echo_skb_max_tx = fifodep;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "xlnx,can-rx-dpth", &fifodep);
+ if (ret < 0)
+ goto err_free;
+ priv->xcan_echo_skb_max_rx = fifodep;
+ }
+
+ ret = clk_prepare_enable(priv->devclk);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable device clock\n");
+ goto err_free;
+ }
+
+ ret = clk_prepare_enable(priv->aperclk);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable aper clock\n");
+ goto err_unprepar_disabledev;
+ }
+
+ priv->can.clock.freq = clk_get_rate(priv->devclk);
+
+ netif_napi_add(ndev, &priv->napi, xcan_rx_poll,
+ priv->xcan_echo_skb_max_rx);
+ ret = register_candev(ndev);
+ if (ret) {
+ dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
+ goto err_unprepar_disableaper;
+ }
+
+ devm_can_led_init(ndev);
+ dev_info(&pdev->dev,
+ "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
+ priv->reg_base, ndev->irq, priv->can.clock.freq,
+ priv->xcan_echo_skb_max_tx);
+
+ return 0;
+
+err_unprepar_disableaper:
+ clk_disable_unprepare(priv->aperclk);
+err_unprepar_disabledev:
+ clk_disable_unprepare(priv->devclk);
+err_free:
+ free_candev(ndev);
+
+ return ret;
+}
+
+/**
+ * xcan_remove - Unregister the device after releasing the resources
+ * @pdev: Handle to the platform device structure
+ *
+ * This function frees all the resources allocated to the device.
+ * Return: 0 always
+ */
+static int xcan_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct xcan_priv *priv = netdev_priv(ndev);
+
+ if (set_reset_mode(ndev) < 0)
+ netdev_err(ndev, "mode resetting failed!\n");
+
+ unregister_candev(ndev);
+ netif_napi_del(&priv->napi);
+ clk_disable_unprepare(priv->aperclk);
+ clk_disable_unprepare(priv->devclk);
+
+ free_candev(ndev);
+
+ return 0;
+}
+
+/* Match table for OF platform binding */
+static struct of_device_id xcan_of_match[] = {
+ { .compatible = "xlnx,zynq-can-1.00.a", },
+ { .compatible = "xlnx,axi-can-1.00.a", },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, xcan_of_match);
+
+static struct platform_driver xcan_driver = {
+ .probe = xcan_probe,
+ .remove = xcan_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .pm = &xcan_dev_pm_ops,
+ .of_match_table = xcan_of_match,
+ },
+};
+
+module_platform_driver(xcan_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Xilinx Inc");
+MODULE_DESCRIPTION("Xilinx CAN interface");
--
1.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
* [PATCH v4 4/5] Documentation: devicetree: sja1000: add reg-io-width binding
From: Marc Kleine-Budde @ 2014-02-06 11:09 UTC (permalink / raw)
To: linux-can
Cc: netdev, Florian Vaussard, Grant Likely, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, devicetree,
Marc Kleine-Budde
In-Reply-To: <1391684996-612-1-git-send-email-mkl@pengutronix.de>
From: Florian Vaussard <florian.vaussard@epfl.ch>
Add the reg-io-width property to describe the width of the memory
accesses.
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: devicetree@vger.kernel.org
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
Tested-by: Andreas Larsson <andreas@gaisler.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
Documentation/devicetree/bindings/net/can/sja1000.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/can/sja1000.txt b/Documentation/devicetree/bindings/net/can/sja1000.txt
index f2105a4..b4a6d53 100644
--- a/Documentation/devicetree/bindings/net/can/sja1000.txt
+++ b/Documentation/devicetree/bindings/net/can/sja1000.txt
@@ -12,6 +12,10 @@ Required properties:
Optional properties:
+- reg-io-width : Specify the size (in bytes) of the IO accesses that
+ should be performed on the device. Valid value is 1, 2 or 4.
+ Default to 1 (8 bits).
+
- nxp,external-clock-frequency : Frequency of the external oscillator
clock in Hz. Note that the internal clock frequency used by the
SJA1000 is half of that value. If not specified, a default value
--
1.8.5.3
^ permalink raw reply related
* Re: [PATCH v2 06/15] dt: binding: add binding for ImgTec IR block
From: Mauro Carvalho Chehab @ 2014-02-06 11:24 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
devicetree, Rob Landley, Tomasz Figa
Cc: James Hogan, linux-media, linux-doc
In-Reply-To: <1389967140-20704-7-git-send-email-james.hogan@imgtec.com>
Em Fri, 17 Jan 2014 13:58:51 +0000
James Hogan <james.hogan@imgtec.com> escreveu:
> Add device tree binding for ImgTec Consumer Infrared block, specifically
> major revision 1 of the hardware.
@DT maintainers:
ping.
>
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
> Cc: linux-media@vger.kernel.org
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Pawel Moll <pawel.moll@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
> Cc: Kumar Gala <galak@codeaurora.org>
> Cc: devicetree@vger.kernel.org
> Cc: Rob Landley <rob@landley.net>
> Cc: linux-doc@vger.kernel.org
> Cc: Tomasz Figa <tomasz.figa@gmail.com>
> ---
> v2:
> - Future proof compatible string from "img,ir" to "img,ir1", where the 1
> corresponds to the major revision number of the hardware (Tomasz
> Figa).
> - Added clock-names property and three specific clock names described in
> the manual, only one of which is used by the current driver (Tomasz
> Figa).
> ---
> .../devicetree/bindings/media/img-ir1.txt | 30 ++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/img-ir1.txt
>
> diff --git a/Documentation/devicetree/bindings/media/img-ir1.txt b/Documentation/devicetree/bindings/media/img-ir1.txt
> new file mode 100644
> index 0000000..ace5fd9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/img-ir1.txt
> @@ -0,0 +1,30 @@
> +* ImgTec Infrared (IR) decoder version 1
> +
> +This binding is for Imagination Technologies' Infrared decoder block,
> +specifically major revision 1.
> +
> +Required properties:
> +- compatible: Should be "img,ir1"
> +- reg: Physical base address of the controller and length of
> + memory mapped region.
> +- interrupts: The interrupt specifier to the cpu.
> +
> +Optional properties:
> +- clocks: List of clock specifiers as described in standard
> + clock bindings.
> +- clock-names: List of clock names corresponding to the clocks
> + specified in the clocks property.
> + Accepted clock names are:
> + "core": Core clock (defaults to 32.768KHz if omitted).
> + "sys": System side (fast) clock.
> + "mod": Power modulation clock.
> +
> +Example:
> +
> + ir@02006200 {
> + compatible = "img,ir1";
> + reg = <0x02006200 0x100>;
> + interrupts = <29 4>;
> + clocks = <&clk_32khz>;
> + clock-names = "core";
> + };
--
Cheers,
Mauro
^ permalink raw reply
* Re: [PATCH 4/8] regulator: da9055: Add DT support
From: Opensource [Adam Thomson] @ 2014-02-06 11:31 UTC (permalink / raw)
To: Mark Brown, Opensource [Adam Thomson]
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
Alessandro Zummo, Linus Walleij, Dmitry Torokhov,
linux-kernel@vger.kernel.org, Rob Herring, Lee Jones,
Guenter Roeck
In-Reply-To: <20140205183642.GH32298@sirena.org.uk>
On Wed, Feb 05, 2014 at 18:37:21PM +0000, Mark Brown wrote:
> On Wed, Feb 05, 2014 at 05:48:35PM +0000, Adam Thomson wrote:
>
> > +#ifdef CONFIG_OF
> > +#include <linux/of.h>
> > +#include <linux/regulator/of_regulator.h>
> > +#endif /* CONFIG_OF */
>
> Don't do ifdefs for includes like this, it's not worth it.
Fine. Seen examples of both in the kernel, but happy to remove it.
>
> > + for_each_child_of_node(nproot, np) {
> > + if (!of_node_cmp(np->name,
> > + regulator->info->reg_desc.name)) {
> > + config->init_data = of_get_regulator_init_data(
> > + &pdev->dev, np);
> > + config->of_node = np;
> > + break;
> > + }
> > + }
>
> I think you're looking for of_regulator_match() here.
Used another driver as an example for this, but if there's a better method
then I'm happy to use it. Will have a look.
>
> > if (pdata && pdata->regulators)
> > config.init_data = pdata->regulators[pdev->id];
> > + else {
> > + ret = da9055_regulator_dt_init(pdev, regulator, &config);
> > + if (ret < 0)
> > + return ret;
> > + }
>
> Coding style, both sides of the if should have braces if one does.
Fine. Will update.
>
> > /* Only LDO 5 and 6 has got the over current interrupt */
> > if (pdev->id == DA9055_ID_LDO5 || pdev->id == DA9055_ID_LDO6) {
> > - irq = platform_get_irq_byname(pdev, "REGULATOR");
> > - irq = regmap_irq_get_virq(da9055->irq_data, irq);
> > + irq = regmap_irq_get_virq(da9055->irq_data,
> > + DA9055_IRQ_REGULATOR);
>
> This seems like a bit of a step backwards - what happened in the MFD
> (and why didn't it update the users to avoid breaking bisection)?
I tested this on target, when doing tests for devicetree. What was happening was
that platform_get_irq_byname() was returning the VIRQ number already (368 in one
test case where onkey was being probed) rather than the local IRQ number for the
device (the resource information seemed to have been updated with the VIRQ
number instead of the local IRQ number). So when that was passed to
regmap_irq_get_virq() it would then return an incorrect IRQ number (0 in the
same scenario, when I enabled DEBUG in irqdomain.c, I would see the message
"error: hwirq 0x170 is too large for da9055_irq"). That incorrect irq was then
being passed to devm_request_threaded_irq() which subsequently failed. This is
why I made the change. Is it preferrable to use platform_get_irq_byname()
instead of regmap_irq_get_virq() as using them both doesn't seem to work, unless
I'm missing something fundamental here.
^ permalink raw reply
* RE: [PATCH 0/8] da9055: Driver initialisation fixes, add DT support
From: Opensource [Adam Thomson] @ 2014-02-06 11:46 UTC (permalink / raw)
To: Guenter Roeck, Opensource [Adam Thomson]
Cc: Mark Brown, Lee Jones,
alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
Linus Walleij, Dmitry Torokhov, Alessandro Zummo
In-Reply-To: <20140206005406.GC1308-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
On Thu, Feb 06, 2014 at 00:54:11AM +0000, Guenter Roeck wrote:
> Adam,
>
> You don't really explain what the problem actually is. Can you elaborate ?
Sorry, yes. For the conflicting device Ids, both the PMIC and the CODEC used
the same I2C Id string, which meant if you tried to intiate both together on the
same bus, then the second would fail.
For the removal of platform_get_irq_byname(), the reason for this was that it
was conflicting with regmap_irq_get_virq() when the IRQ value returned from
platform_get_irq_byname() was being passed to regmap_irq_get_virq(). The result
for the code was that it would try to request a threaded IRQ using an invalid
IRQ number (have also described this further in patch 0004 mail thread,
https://lkml.org/lkml/2014/2/6/126).
>
> Also, I have been using platform_get_irq() to get the interrupt resource
> in mfd client drivers and similar situations. Wouldn't this work here as well
> if you don't want to use platform_get_irq_byname() ?
What I could've done is use platform_get_irq_byname() and avoided using
regmap_irq_get_virq() as I would already have the correct VIRQ to pass to
request_threaded_irq(), but I figured that using regmap_irq_get_virq() made
more sense at the time, and was unable to use both.
^ permalink raw reply
* Re: [PATCH v3] sh_eth: add device tree support
From: Sergei Shtylyov @ 2014-02-06 11:52 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Rob Herring, Pawel Moll, Mark Rutland, Grant Likely,
devicetree@vger.kernel.org, Linux-sh list, Ian Campbell,
Kumar Gala, Nobuhiro Iwamatsu, Rob Landley,
linux-doc@vger.kernel.org
In-Reply-To: <CAMuHMdUrAJOTvUPZXVVK3ZSWL1BhE=uiZq0Yxp8XQuRaAxYnKw@mail.gmail.com>
Hello.
On 06-02-2014 12:16, Geert Uytterhoeven wrote:
>> +- reg: offset and length of (1) the E-DMAC/feLic register block (required),
>> + (2) the TSU register block (optional).
> As there can be 2 regions, have you considered reg-names?
I've been asked already, and so considered them and found them unneeded.
> Gr{oetje,eeting}s,
> Geert
WBR, Sergei
^ permalink raw reply
* Disabling autoneg and enforcing speed/duplex
From: Gerlando Falauto @ 2014-02-06 12:00 UTC (permalink / raw)
To: devicetree@vger.kernel.org, netdev@vger.kernel.org
Cc: Grant Likely, Sebastian Hesselbarth, Brunck, Holger,
Florian Fainelli
Hi,
I'm using the Kirkwood Ethernet controller (mv643xx_eth.c) with a
Marvell 88E3018 PHY which needs to be set in forced 100Base-TX mode.
Thanks to Sebastian's addition of DT support to the ethernet driver, I
can easily set speed and duplex within the ethernet's port node,
therefore leaving the phy unmanaged -- this works fine (I guess this
mode is set on the phy by the bootloader or by strap settings).
However, this PHY has an erratum whose workaround requires some
registers be written -- I believe the natural solution would be to start
managing the PHY (i.e. set "phy-handle") and implement the proper
workaround within drivers/net/phy/marvell.c. Making the PHY managed does
however enable autoneg and therefore break everything.
Which brings me to my question: shouldn't there be a way to specify some
forced settings within the PHY's node for such cases?
Only thing I found vaguely resembling what I'm looking for is Florian's
patch introducing "max-speed" in the PHY -- not quite the same thing
though. Which, if I understand it correctly, implements it as a property
of the PHY, whereas ePAPR specifies it as a property of the ethernet
device (which makes sense, since you might want to connect a 10/100 MII
to a 10/100/1000 PHY and therefore have the MII restrict the
capabilities of the PHY).
Or perhaps I'm missing some important bits here?
Thanks!
Gerlando
^ permalink raw reply
* Re: [PATCH 4/8] regulator: da9055: Add DT support
From: Mark Brown @ 2014-02-06 12:03 UTC (permalink / raw)
To: Opensource [Adam Thomson]
Cc: Lee Jones, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
Linus Walleij, Dmitry Torokhov, Alessandro Zummo, Guenter Roeck
In-Reply-To: <2E89032DDAA8B9408CB92943514A03376B7E1669-68WUHU125fLzLL1Oxlh9IgLouzNaz+3S@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 2455 bytes --]
On Thu, Feb 06, 2014 at 11:31:13AM +0000, Opensource [Adam Thomson] wrote:
> On Wed, Feb 05, 2014 at 18:37:21PM +0000, Mark Brown wrote:
> > > + for_each_child_of_node(nproot, np) {
> > > + if (!of_node_cmp(np->name,
> > > + regulator->info->reg_desc.name)) {
> > > + config->init_data = of_get_regulator_init_data(
> > > + &pdev->dev, np);
> > > + config->of_node = np;
> > > + break;
> > > + }
> > > + }
> > I think you're looking for of_regulator_match() here.
> Used another driver as an example for this, but if there's a better method
> then I'm happy to use it. Will have a look.
That's probably an older driver - the code was factored out at some
point but lots of drivers don't get much love.
> > > /* Only LDO 5 and 6 has got the over current interrupt */
> > > if (pdev->id == DA9055_ID_LDO5 || pdev->id == DA9055_ID_LDO6) {
> > > - irq = platform_get_irq_byname(pdev, "REGULATOR");
> > > - irq = regmap_irq_get_virq(da9055->irq_data, irq);
> > > + irq = regmap_irq_get_virq(da9055->irq_data,
> > > + DA9055_IRQ_REGULATOR);
> > This seems like a bit of a step backwards - what happened in the MFD
> > (and why didn't it update the users to avoid breaking bisection)?
> I tested this on target, when doing tests for devicetree. What was happening was
> that platform_get_irq_byname() was returning the VIRQ number already (368 in one
> test case where onkey was being probed) rather than the local IRQ number for the
> device (the resource information seemed to have been updated with the VIRQ
> number instead of the local IRQ number). So when that was passed to
> regmap_irq_get_virq() it would then return an incorrect IRQ number (0 in the
> same scenario, when I enabled DEBUG in irqdomain.c, I would see the message
> "error: hwirq 0x170 is too large for da9055_irq"). That incorrect irq was then
> being passed to devm_request_threaded_irq() which subsequently failed. This is
> why I made the change. Is it preferrable to use platform_get_irq_byname()
> instead of regmap_irq_get_virq() as using them both doesn't seem to work, unless
> I'm missing something fundamental here.
What's happening here is that the MFD framework has done the lookup for
you when passing the interrupt resource through - you should just use
platform_get_irq_byname() and save a little code in the driver. If it's
behaving differently on DT and non-DT systems it seems better to figure
out why and then make it consistent.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* RE: [PATCH 4/8] regulator: da9055: Add DT support
From: Opensource [Adam Thomson] @ 2014-02-06 12:22 UTC (permalink / raw)
To: Mark Brown, Opensource [Adam Thomson]
Cc: Lee Jones, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
Linus Walleij, Dmitry Torokhov, Alessandro Zummo, Guenter Roeck
In-Reply-To: <20140206120336.GL32298-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
On Thu, Feb 06, 2014 at 12:04:52PM +0000, Mark Brown wrote:
> > Used another driver as an example for this, but if there's a better method
> > then I'm happy to use it. Will have a look.
>
> That's probably an older driver - the code was factored out at some
> point but lots of drivers don't get much love.
Yep, fair enough. Will sort it.
> What's happening here is that the MFD framework has done the lookup for
> you when passing the interrupt resource through - you should just use
> platform_get_irq_byname() and save a little code in the driver. If it's
> behaving differently on DT and non-DT systems it seems better to figure
> out why and then make it consistent.
Is what I summised but I obviously chose the wrong direction for my fix.
Will use platform_get_irq_byname() solely, and will make sure all works on
both DT and non-DT setups. If not I'll dig further.
^ permalink raw reply
* [PATCH] ASoC: pcm512x: Add PCM512x driver
From: Mark Brown @ 2014-02-06 12:26 UTC (permalink / raw)
To: Florian Meier, Liam Girdwood
Cc: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
devicetree-u79uwXL29TY76Z2rM5mHXA, Mark Brown
From: Mark Brown <broonie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
The PCM512x devices are a family of monolithic CMOS integrated circuits
that include a stereo digital-to-analog converter and additional support
circuitry.
This is an initial driver which supports some core functionality for the
device which covers common use cases but does not cover all features.
Currently only slave clocking modes with automatic clock configuration
are supported and most of the DSP configuration for the device is not
enabled.
Signed-off-by: Mark Brown <broonie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
.../devicetree/bindings/sound/pcm512x.txt | 30 +
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/pcm512x.c | 671 +++++++++++++++++++++
sound/soc/codecs/pcm512x.h | 142 +++++
5 files changed, 849 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/pcm512x.txt
create mode 100644 sound/soc/codecs/pcm512x.c
create mode 100644 sound/soc/codecs/pcm512x.h
diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt
new file mode 100644
index 000000000000..faff75e64573
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/pcm512x.txt
@@ -0,0 +1,30 @@
+PCM512x audio CODECs
+
+These devices support both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+ - compatible : One of "ti,pcm5121" or "ti,pcm5122"
+
+ - reg : the I2C address of the device for I2C, the chip select
+ number for SPI.
+
+ - AVDD-supply, DVDD-supply, and CPVDD-supply : power supplies for the
+ device, as covered in bindings/regulator/regulator.txt
+
+Optional properties:
+
+ - clocks : A clock specifier for the clock connected as SCLK. If this
+ is absent the device will be configured to clock from BCLK.
+
+Example:
+
+ pcm5122: pcm5122@4c {
+ compatible = "ti,pcm5122";
+ reg = <0x4c>;
+
+ AVDD-supply = <®_3v3_analog>;
+ DVDD-supply = <®_1v8>;
+ CPVDD-supply = <®_3v3>;
+ };
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f0bdcc5abe83..28faa2d0eaef 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -60,6 +60,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
+ select SND_SOC_PCM512x if SND_SOC_I2C_AND_SPI
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_SGTL5000 if I2C
@@ -324,6 +325,9 @@ config SND_SOC_PCM1792A
config SND_SOC_PCM3008
tristate
+config SND_SOC_PCM512x
+ tristate "Texas Instruments PCM512x CODECs"
+
config SND_SOC_RT5631
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index bc126764a44d..d3b536fc075d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -46,6 +46,7 @@ snd-soc-hdmi-codec-objs := hdmi.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-pcm512x-objs := pcm512x.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-sgtl5000-objs := sgtl5000.o
@@ -179,6 +180,7 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
new file mode 100644
index 000000000000..886e530ad2d2
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.c
@@ -0,0 +1,671 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ * Copyright 2014 Linaro Ltd
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "pcm512x.h"
+
+#define PCM512x_NUM_SUPPLIES 3
+static const char *pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
+ "AVDD",
+ "DVDD",
+ "CPVDD",
+};
+
+struct pcm512x_priv {
+ struct regmap *regmap;
+ struct clk *sclk;
+ struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
+ struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
+};
+
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define PCM512x_REGULATOR_EVENT(n) \
+static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
+ supply_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(pcm512x->regmap); \
+ regcache_cache_only(pcm512x->regmap, true); \
+ } \
+ return 0; \
+}
+
+PCM512x_REGULATOR_EVENT(0)
+PCM512x_REGULATOR_EVENT(1)
+PCM512x_REGULATOR_EVENT(2)
+
+static const struct reg_default pcm512x_reg_defaults[] = {
+ { PCM512x_RESET, 0x00 },
+ { PCM512x_POWER, 0x00 },
+ { PCM512x_MUTE, 0x00 },
+ { PCM512x_DSP, 0x00 },
+ { PCM512x_PLL_REF, 0x00 },
+ { PCM512x_DAC_ROUTING, 0x11 },
+ { PCM512x_DSP_PROGRAM, 0x01 },
+ { PCM512x_CLKDET, 0x00 },
+ { PCM512x_AUTO_MUTE, 0x00 },
+ { PCM512x_ERROR_DETECT, 0x00 },
+ { PCM512x_DIGITAL_VOLUME_1, 0x00 },
+ { PCM512x_DIGITAL_VOLUME_2, 0x30 },
+ { PCM512x_DIGITAL_VOLUME_3, 0x30 },
+ { PCM512x_DIGITAL_MUTE_1, 0x22 },
+ { PCM512x_DIGITAL_MUTE_2, 0x00 },
+ { PCM512x_DIGITAL_MUTE_3, 0x07 },
+};
+
+static bool pcm512x_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM512x_RESET:
+ case PCM512x_POWER:
+ case PCM512x_MUTE:
+ case PCM512x_PLL_EN:
+ case PCM512x_SPI_MISO_FUNCTION:
+ case PCM512x_DSP:
+ case PCM512x_GPIO_EN:
+ case PCM512x_BCLK_LRCLK_CFG:
+ case PCM512x_DSP_GPIO_INPUT:
+ case PCM512x_MASTER_MODE:
+ case PCM512x_PLL_REF:
+ case PCM512x_PLL_COEFF_0:
+ case PCM512x_PLL_COEFF_1:
+ case PCM512x_PLL_COEFF_2:
+ case PCM512x_PLL_COEFF_3:
+ case PCM512x_PLL_COEFF_4:
+ case PCM512x_DSP_CLKDIV:
+ case PCM512x_DAC_CLKDIV:
+ case PCM512x_NCP_CLKDIV:
+ case PCM512x_OSR_CLKDIV:
+ case PCM512x_MASTER_CLKDIV_1:
+ case PCM512x_MASTER_CLKDIV_2:
+ case PCM512x_FS_SPEED_MODE:
+ case PCM512x_IDAC_1:
+ case PCM512x_IDAC_2:
+ case PCM512x_ERROR_DETECT:
+ case PCM512x_I2S_1:
+ case PCM512x_I2S_2:
+ case PCM512x_DAC_ROUTING:
+ case PCM512x_DSP_PROGRAM:
+ case PCM512x_CLKDET:
+ case PCM512x_AUTO_MUTE:
+ case PCM512x_DIGITAL_VOLUME_1:
+ case PCM512x_DIGITAL_VOLUME_2:
+ case PCM512x_DIGITAL_VOLUME_3:
+ case PCM512x_DIGITAL_MUTE_1:
+ case PCM512x_DIGITAL_MUTE_2:
+ case PCM512x_DIGITAL_MUTE_3:
+ case PCM512x_GPIO_OUTPUT_1:
+ case PCM512x_GPIO_OUTPUT_2:
+ case PCM512x_GPIO_OUTPUT_3:
+ case PCM512x_GPIO_OUTPUT_4:
+ case PCM512x_GPIO_OUTPUT_5:
+ case PCM512x_GPIO_OUTPUT_6:
+ case PCM512x_GPIO_CONTROL_1:
+ case PCM512x_GPIO_CONTROL_2:
+ case PCM512x_OVERFLOW:
+ case PCM512x_RATE_DET_1:
+ case PCM512x_RATE_DET_2:
+ case PCM512x_RATE_DET_3:
+ case PCM512x_RATE_DET_4:
+ case PCM512x_ANALOG_MUTE_DET:
+ case PCM512x_GPIN:
+ case PCM512x_DIGITAL_MUTE_DET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool pcm512x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM512x_PLL_EN:
+ case PCM512x_OVERFLOW:
+ case PCM512x_RATE_DET_1:
+ case PCM512x_RATE_DET_2:
+ case PCM512x_RATE_DET_3:
+ case PCM512x_RATE_DET_4:
+ case PCM512x_ANALOG_MUTE_DET:
+ case PCM512x_GPIN:
+ case PCM512x_DIGITAL_MUTE_DET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
+
+static const char *pcm512x_dsp_program_texts[] = {
+ "FIR interpolation with de-emphasis",
+ "Low latency IIR with de-emphasis",
+ "High attenuation with de-emphasis",
+ "Ringing-less low latency FIR",
+};
+
+static const unsigned int pcm512x_dsp_program_values[] = {
+ 1,
+ 2,
+ 3,
+ 5,
+ 7,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
+ PCM512x_DSP_PROGRAM, 0, 0x1f,
+ pcm512x_dsp_program_texts,
+ pcm512x_dsp_program_values);
+
+static const char *pcm512x_clk_missing_text[] = {
+ "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
+};
+
+static const struct soc_enum pcm512x_clk_missing =
+ SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 7, pcm512x_clk_missing_text);
+
+static const char *pcm512x_autom_text[] = {
+ "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
+};
+
+static const struct soc_enum pcm512x_autom_l =
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 7,
+ pcm512x_autom_text);
+
+static const struct soc_enum pcm512x_autom_r =
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 7,
+ pcm512x_autom_text);
+
+static const char *pcm512x_ramp_rate_text[] = {
+ "1 sample/update", "2 samples/update", "4 samples/update",
+ "Immediate"
+};
+
+static const struct soc_enum pcm512x_vndf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vnuf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vedf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const char *pcm512x_ramp_step_text[] = {
+ "4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
+};
+
+static const struct soc_enum pcm512x_vnds =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_vnus =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_veds =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct snd_kcontrol_new pcm512x_controls[] = {
+SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
+ PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
+SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
+ PCM512x_RQMR_SHIFT, 1, 1),
+
+SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
+SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program),
+
+SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
+SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
+SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
+SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
+ PCM512x_ACTL_SHIFT, 1, 0),
+SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
+ PCM512x_AMLR_SHIFT, 1, 0),
+
+SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
+SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
+SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
+SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
+SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
+SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
+};
+
+static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
+ { "DACL", NULL, "Playback" },
+ { "DACR", NULL, "Playback" },
+
+ { "OUTL", NULL, "DACL" },
+ { "OUTR", NULL, "DACR" },
+};
+
+static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, 0);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to remove standby: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, PCM512x_RQST);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request standby: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver pcm512x_dai = {
+ .name = "pcm512x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE
+ },
+};
+
+static struct snd_soc_codec_driver pcm512x_codec_driver = {
+ .set_bias_level = pcm512x_set_bias_level,
+ .idle_bias_off = true,
+
+ .controls = pcm512x_controls,
+ .num_controls = ARRAY_SIZE(pcm512x_controls),
+ .dapm_widgets = pcm512x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
+ .dapm_routes = pcm512x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
+};
+
+static const struct regmap_config pcm512x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = pcm512x_readable,
+ .volatile_reg = pcm512x_volatile,
+
+ .max_register = PCM512x_MAX_REGISTER,
+ .reg_defaults = pcm512x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct of_device_id pcm512x_of_match[] = {
+ { .compatible = "ti,pcm5121", },
+ { .compatible = "ti,pcm5122", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+
+static int pcm512x_probe(struct device *dev, struct regmap *regmap)
+{
+ struct pcm512x_priv *pcm512x;
+ int i, ret;
+
+ pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
+ if (!pcm512x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pcm512x);
+ pcm512x->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
+ pcm512x->supplies[i].supply = pcm512x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
+ pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
+ pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
+
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
+ ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
+ &pcm512x->supply_nb[i]);
+ if (ret != 0) {
+ dev_err(dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the device, verifying I/O in the process for I2C */
+ ret = regmap_write(regmap, PCM512x_RESET,
+ PCM512x_RSTM | PCM512x_RSTR);
+ if (ret != 0) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_write(regmap, PCM512x_RESET, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err;
+ }
+
+ pcm512x->sclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(pcm512x->sclk)) {
+ if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_info(dev, "No SCLK, using BCLK: %ld\n",
+ PTR_ERR(pcm512x->sclk));
+
+ /* Disable reporting of missing SCLK as an error */
+ regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
+ PCM512x_IDCH, PCM512x_IDCH);
+
+ /* Switch PLL input to BCLK */
+ regmap_update_bits(regmap, PCM512x_PLL_REF,
+ PCM512x_SREF, PCM512x_SREF);
+ } else {
+ ret = clk_prepare_enable(pcm512x->sclk);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable SCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Default to standby mode */
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, PCM512x_RQST);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request standby: %d\n",
+ ret);
+ goto err_clk;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
+ &pcm512x_dai, 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_pm;
+ }
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(dev);
+err_clk:
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+err:
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ return ret;
+}
+
+static void pcm512x_remove(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+
+ snd_soc_unregister_codec(dev);
+ pm_runtime_disable(dev);
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+}
+
+static int pcm512x_suspend(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQPD, PCM512x_RQPD);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request power down: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+
+ return 0;
+}
+
+static int pcm512x_resume(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+ int ret;
+
+ if (!IS_ERR(pcm512x->sclk)) {
+ ret = clk_prepare_enable(pcm512x->sclk);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable SCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(pcm512x->regmap, false);
+ ret = regcache_sync(pcm512x->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQPD, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to remove power down: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops pcm512x_pm_ops = {
+ SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+static int pcm512x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm512x_probe(&i2c->dev, regmap);
+}
+
+static int pcm512x_i2c_remove(struct i2c_client *i2c)
+{
+ pcm512x_remove(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id pcm512x_i2c_id[] = {
+ { "pcm5121", },
+ { "pcm5122", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
+
+static struct i2c_driver pcm512x_i2c_driver = {
+ .probe = pcm512x_i2c_probe,
+ .remove = pcm512x_i2c_remove,
+ .id_table = pcm512x_i2c_id,
+ .driver = {
+ .name = "pcm512x",
+ .owner = THIS_MODULE,
+ .of_match_table = pcm512x_of_match,
+ .pm = &pcm512x_pm_ops,
+ },
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int pcm512x_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_spi(spi, &pcm512x_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ return ret;
+ }
+
+ return pcm512x_probe(&spi->dev, regmap);
+}
+
+static int pcm512x_spi_remove(struct spi_device *spi)
+{
+ pcm512x_remove(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id pcm512x_spi_id[] = {
+ { "pcm5121", },
+ { "pcm5122", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
+
+static struct spi_driver pcm512x_spi_driver = {
+ .probe = pcm512x_spi_probe,
+ .remove = pcm512x_spi_remove,
+ .id_table = pcm512x_spi_id,
+ .driver = {
+ .name = "pcm512x",
+ .owner = THIS_MODULE,
+ .of_match_table = pcm512x_of_match,
+ .pm = &pcm512x_pm_ops,
+ },
+};
+#endif
+
+static int __init pcm512x_modinit(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&pcm512x_i2c_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register pcm512x I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&pcm512x_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register pcm512x SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(pcm512x_modinit);
+
+static void __exit pcm512x_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&pcm512x_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&pcm512x_spi_driver);
+#endif
+}
+module_exit(pcm512x_exit);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver");
+MODULE_AUTHOR("Mark Brown <broonie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
new file mode 100644
index 000000000000..b2f518ecb35c
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.h
@@ -0,0 +1,142 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ * Copyright 2014 Linaro Ltd
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _SND_SOC_PCM512X
+#define _SND_SOC_PCM512X
+
+#define PCM512x_PAGE_0_BASE 0
+
+#define PCM512x_PAGE 0
+
+#define PCM512x_RESET (PCM512x_PAGE_0_BASE + 1)
+#define PCM512x_POWER (PCM512x_PAGE_0_BASE + 2)
+#define PCM512x_MUTE (PCM512x_PAGE_0_BASE + 3)
+#define PCM512x_PLL_EN (PCM512x_PAGE_0_BASE + 4)
+#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_0_BASE + 6)
+#define PCM512x_DSP (PCM512x_PAGE_0_BASE + 7)
+#define PCM512x_GPIO_EN (PCM512x_PAGE_0_BASE + 8)
+#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_0_BASE + 9)
+#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_0_BASE + 10)
+#define PCM512x_MASTER_MODE (PCM512x_PAGE_0_BASE + 12)
+#define PCM512x_PLL_REF (PCM512x_PAGE_0_BASE + 13)
+#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_0_BASE + 20)
+#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_0_BASE + 21)
+#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_0_BASE + 22)
+#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_0_BASE + 23)
+#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_0_BASE + 24)
+#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_0_BASE + 27)
+#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_0_BASE + 28)
+#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_0_BASE + 29)
+#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_0_BASE + 30)
+#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_0_BASE + 32)
+#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_0_BASE + 33)
+#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_0_BASE + 34)
+#define PCM512x_IDAC_1 (PCM512x_PAGE_0_BASE + 35)
+#define PCM512x_IDAC_2 (PCM512x_PAGE_0_BASE + 36)
+#define PCM512x_ERROR_DETECT (PCM512x_PAGE_0_BASE + 37)
+#define PCM512x_I2S_1 (PCM512x_PAGE_0_BASE + 40)
+#define PCM512x_I2S_2 (PCM512x_PAGE_0_BASE + 41)
+#define PCM512x_DAC_ROUTING (PCM512x_PAGE_0_BASE + 42)
+#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_0_BASE + 43)
+#define PCM512x_CLKDET (PCM512x_PAGE_0_BASE + 44)
+#define PCM512x_AUTO_MUTE (PCM512x_PAGE_0_BASE + 59)
+#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_0_BASE + 60)
+#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_0_BASE + 61)
+#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_0_BASE + 62)
+#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_0_BASE + 63)
+#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_0_BASE + 64)
+#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_0_BASE + 65)
+#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_0_BASE + 80)
+#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_0_BASE + 81)
+#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_0_BASE + 82)
+#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_0_BASE + 83)
+#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_0_BASE + 84)
+#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_0_BASE + 85)
+#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_0_BASE + 86)
+#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_0_BASE + 87)
+#define PCM512x_OVERFLOW (PCM512x_PAGE_0_BASE + 90)
+#define PCM512x_RATE_DET_1 (PCM512x_PAGE_0_BASE + 91)
+#define PCM512x_RATE_DET_2 (PCM512x_PAGE_0_BASE + 92)
+#define PCM512x_RATE_DET_3 (PCM512x_PAGE_0_BASE + 93)
+#define PCM512x_RATE_DET_4 (PCM512x_PAGE_0_BASE + 94)
+#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_0_BASE + 108)
+#define PCM512x_GPIN (PCM512x_PAGE_0_BASE + 119)
+#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_0_BASE + 120)
+
+#define PCM512x_MAX_REGISTER (PCM512x_PAGE_0_BASE + 120)
+
+/* Page 0, Register 1 - reset */
+#define PCM512x_RSTR (1 << 0)
+#define PCM512x_RSTM (1 << 4)
+
+/* Page 0, Register 2 - power */
+#define PCM512x_RQPD (1 << 0)
+#define PCM512x_RQPD_SHIFT 0
+#define PCM512x_RQST (1 << 4)
+#define PCM512x_RQST_SHIFT 4
+
+/* Page 0, Register 3 - mute */
+#define PCM512x_RQMR_SHIFT 0
+#define PCM512x_RQML_SHIFT 4
+
+/* Page 0, Register 4 - PLL */
+#define PCM512x_PLCE (1 << 0)
+#define PCM512x_RLCE_SHIFT 0
+#define PCM512x_PLCK (1 << 4)
+#define PCM512x_PLCK_SHIFT 4
+
+/* Page 0, Register 7 - DSP */
+#define PCM512x_SDSL (1 << 0)
+#define PCM512x_SDSL_SHIFT 0
+#define PCM512x_DEMP (1 << 4)
+#define PCM512x_DEMP_SHIFT 4
+
+/* Page 0, Register 13 - PLL reference */
+#define PCM512x_SREF (1 << 4)
+
+/* Page 0, Register 37 - Error detection */
+#define PCM512x_IPLK (1 << 0)
+#define PCM512x_DCAS (1 << 1)
+#define PCM512x_IDCM (1 << 2)
+#define PCM512x_IDCH (1 << 3)
+#define PCM512x_IDSK (1 << 4)
+#define PCM512x_IDBK (1 << 5)
+#define PCM512x_IDFS (1 << 6)
+
+/* Page 0, Register 42 - DAC routing */
+#define PCM512x_AUPR_SHIFT 0
+#define PCM512x_AUPL_SHIFT 4
+
+/* Page 0, Register 59 - auto mute */
+#define PCM512x_ATMR_SHIFT 0
+#define PCM512x_ATML_SHIFT 4
+
+/* Page 0, Register 63 - ramp rates */
+#define PCM512x_VNDF_SHIFT 6
+#define PCM512x_VNDS_SHIFT 4
+#define PCM512x_VNUF_SHIFT 2
+#define PCM512x_VNUS_SHIFT 0
+
+/* Page 0, Register 64 - emergency ramp rates */
+#define PCM512x_VEDF_SHIFT 6
+#define PCM512x_VEDS_SHIFT 4
+
+/* Page 0, Register 65 - Digital mute enables */
+#define PCM512x_ACTL_SHIFT 2
+#define PCM512x_AMLE_SHIFT 1
+#define PCM512x_AMLR_SHIFT 0
+
+#endif
--
1.9.rc1
--
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] can: xilinx CAN controller support.
From: Marc Kleine-Budde @ 2014-02-06 12:40 UTC (permalink / raw)
To: Kedareswara rao Appana, wg, michal.simek, grant.likely, robh+dt,
linux-can
Cc: netdev, linux-arm-kernel, linux-kernel, devicetree,
Kedareswara rao Appana
In-Reply-To: <6c2bcce0-9897-4d1d-a8b9-47924e40f73c@VA3EHSMHS008.ehs.local>
[-- Attachment #1: Type: text/plain, Size: 44985 bytes --]
On 02/06/2014 11:19 AM, Kedareswara rao Appana wrote:
> This patch adds xilinx CAN controller support.
> This driver supports both ZYNQ CANPS IP and
> Soft IP AXI CAN controller.
>
> Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
First review, see comments inline.
Marc
> ---
> This patch is rebased on the 3.14 rc1 kernel.
> ---
> .../devicetree/bindings/net/can/xilinx_can.txt | 43 +
> drivers/net/can/Kconfig | 8 +
> drivers/net/can/Makefile | 1 +
> drivers/net/can/xilinx_can.c | 1150 ++++++++++++++++++++
> 4 files changed, 1202 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/net/can/xilinx_can.txt
> create mode 100644 drivers/net/can/xilinx_can.c
>
> diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
> new file mode 100644
> index 0000000..34f9643
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
> @@ -0,0 +1,43 @@
> +Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
> +---------------------------------------------------------
> +
> +Required properties:
> +- compatible : Should be "xlnx,zynq-can-1.00.a" for Zynq CAN
> + controllers and "xlnx,axi-can-1.00.a" for Axi CAN
> + controllers.
> +- reg : Physical base address and size of the Axi CAN/Zynq
> + CANPS registers map.
> +- interrupts : Property with a value describing the interrupt
> + number.
> +- interrupt-parent : Must be core interrupt controller
> +- clock-names : List of input clock names - "ref_clk", "aper_clk"
> + (See clock bindings for details. Two clocks are
> + required for Zynq CAN. For Axi CAN
> + case it is one(ref_clk)).
> +- clocks : Clock phandles (see clock bindings for details).
> +- xlnx,can-tx-dpth : Can Tx fifo depth (Required for Axi CAN).
> +- xlnx,can-rx-dpth : Can Rx fifo depth (Required for Axi CAN).
> +
> +
> +Example:
> +
> +For Zynq CANPS Dts file:
> + zynq_can_0: zynq-can@e0008000 {
> + compatible = "xlnx,zynq-can-1.00.a";
> + clocks = <&clkc 19>, <&clkc 36>;
> + clock-names = "ref_clk", "aper_clk";
> + reg = <0xe0008000 0x1000>;
> + interrupts = <0 28 4>;
> + interrupt-parent = <&intc>;
Above xlnx,can-{rx,tx}-dpth is mentioned as required, but it's not in
the Zynq example.
> + };
> +For Axi CAN Dts file:
> + axi_can_0: axi-can@40000000 {
> + compatible = "xlnx,axi-can-1.00.a";
> + clocks = <&clkc 0>;
> + clock-names = "ref_clk" ;
> + reg = <0x40000000 0x10000>;
> + interrupt-parent = <&intc>;
> + interrupts = <0 59 1>;
> + xlnx,can-tx-dpth = <0x40>;
> + xlnx,can-rx-dpth = <0x40>;
> + };
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index d447b88..2344fb5 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -125,6 +125,14 @@ config CAN_GRCAN
> endian syntheses of the cores would need some modifications on
> the hardware level to work.
>
> +config CAN_XILINXCAN
> + tristate "Xilinx CAN"
> + depends on ARCH_ZYNQ || MICROBLAZE
> + default n
"default n" is default, please remove.
> + ---help---
> + Xilinx CAN driver. This driver supports both soft AXI CAN IP and
> + Zynq CANPS IP.
> +
> source "drivers/net/can/mscan/Kconfig"
>
> source "drivers/net/can/sja1000/Kconfig"
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index c744039..0b8e11e 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -25,5 +25,6 @@ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
> obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
> obj-$(CONFIG_PCH_CAN) += pch_can.o
> obj-$(CONFIG_CAN_GRCAN) += grcan.o
> +obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
> new file mode 100644
> index 0000000..c1b2b9d
> --- /dev/null
> +++ b/drivers/net/can/xilinx_can.c
> @@ -0,0 +1,1150 @@
> +/* Xilinx CAN device driver
> + *
> + * Copyright (C) 2012 - 2014 Xilinx, Inc.
> + * Copyright (C) 2009 PetaLogix. All rights reserved.
> + *
> + * Description:
> + * This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/skbuff.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/can/led.h>
> +
> +#define DRIVER_NAME "XILINX_CAN"
> +
> +/* CAN registers set */
> +#define XCAN_SRR_OFFSET 0x00 /* Software reset */
> +#define XCAN_MSR_OFFSET 0x04 /* Mode select */
> +#define XCAN_BRPR_OFFSET 0x08 /* Baud rate prescaler */
> +#define XCAN_BTR_OFFSET 0x0C /* Bit timing */
> +#define XCAN_ECR_OFFSET 0x10 /* Error counter */
> +#define XCAN_ESR_OFFSET 0x14 /* Error status */
> +#define XCAN_SR_OFFSET 0x18 /* Status */
> +#define XCAN_ISR_OFFSET 0x1C /* Interrupt status */
> +#define XCAN_IER_OFFSET 0x20 /* Interrupt enable */
> +#define XCAN_ICR_OFFSET 0x24 /* Interrupt clear */
> +#define XCAN_TXFIFO_ID_OFFSET 0x30 /* TX FIFO ID */
> +#define XCAN_TXFIFO_DLC_OFFSET 0x34 /* TX FIFO DLC */
> +#define XCAN_TXFIFO_DW1_OFFSET 0x38 /* TX FIFO Data Word 1 */
> +#define XCAN_TXFIFO_DW2_OFFSET 0x3C /* TX FIFO Data Word 2 */
> +#define XCAN_RXFIFO_ID_OFFSET 0x50 /* RX FIFO ID */
> +#define XCAN_RXFIFO_DLC_OFFSET 0x54 /* RX FIFO DLC */
> +#define XCAN_RXFIFO_DW1_OFFSET 0x58 /* RX FIFO Data Word 1 */
> +#define XCAN_RXFIFO_DW2_OFFSET 0x5C /* RX FIFO Data Word 2 */
> +
> +/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
> +#define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */
> +#define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */
> +#define XCAN_MSR_LBACK_MASK 0x00000002 /* Loop back mode select */
> +#define XCAN_MSR_SLEEP_MASK 0x00000001 /* Sleep mode select */
> +#define XCAN_BRPR_BRP_MASK 0x000000FF /* Baud rate prescaler */
> +#define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */
> +#define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */
> +#define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */
> +#define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */
> +#define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */
> +#define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */
> +#define XCAN_ESR_BERR_MASK 0x00000008 /* Bit error */
> +#define XCAN_ESR_STER_MASK 0x00000004 /* Stuff error */
> +#define XCAN_ESR_FMER_MASK 0x00000002 /* Form error */
> +#define XCAN_ESR_CRCER_MASK 0x00000001 /* CRC error */
> +#define XCAN_SR_TXFLL_MASK 0x00000400 /* TX FIFO is full */
> +#define XCAN_SR_ESTAT_MASK 0x00000180 /* Error status */
> +#define XCAN_SR_ERRWRN_MASK 0x00000040 /* Error warning */
> +#define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */
> +#define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */
> +#define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */
> +#define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */
> +#define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */
> +#define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */
> +#define XCAN_IXR_BSOFF_MASK 0x00000200 /* Bus off interrupt */
> +#define XCAN_IXR_ERROR_MASK 0x00000100 /* Error interrupt */
> +#define XCAN_IXR_RXNEMP_MASK 0x00000080 /* RX FIFO NotEmpty intr */
> +#define XCAN_IXR_RXOFLW_MASK 0x00000040 /* RX FIFO Overflow intr */
> +#define XCAN_IXR_RXOK_MASK 0x00000010 /* Message received intr */
> +#define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */
> +#define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */
> +#define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */
> +#define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */
> +#define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */
> +#define XCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */
> +#define XCAN_IDR_RTR_MASK 0x00000001 /* Remote TX request */
> +#define XCAN_DLCR_DLC_MASK 0xF0000000 /* Data length code */
> +
> +#define XCAN_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
> + XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
> + XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
> + XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK)
> +
> +/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
> +#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */
> +#define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */
> +#define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */
> +#define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */
> +#define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */
> +#define XCAN_ESR_REC_SHIFT 8 /* Rx Error Count */
> +
> +/* CAN frame length constants */
> +#define XCAN_ECHO_SKB_MAX 64
> +#define XCAN_NAPI_WEIGHT 64
> +#define XCAN_FRAME_MAX_DATA_LEN 8
> +#define XCAN_TIMEOUT (50 * HZ)
> +
> +/**
> + * struct xcan_priv - This definition define CAN driver instance
> + * @can: CAN private data structure.
> + * @open_time: For holding timeout values
> + * @waiting_ech_skb_index: Pointer for skb
> + * @ech_skb_next: This tell the next packet in the queue
> + * @waiting_ech_skb_num: Gives the number of packets waiting
> + * @xcan_echo_skb_max_tx: Maximum number packets the driver can send
> + * @xcan_echo_skb_max_rx: Maximum number packets the driver can receive
> + * @napi: NAPI structure
> + * @ech_skb_lock: For spinlock purpose
> + * @read_reg: For reading data from CAN registers
> + * @write_reg: For writing data to CAN registers
> + * @dev: Network device data structure
> + * @reg_base: Ioremapped address to registers
> + * @irq_flags: For request_irq()
> + * @aperclk: Pointer to struct clk
> + * @devclk: Pointer to struct clk
> + */
> +struct xcan_priv {
> + struct can_priv can;
> + int open_time;
> + int waiting_ech_skb_index;
> + int ech_skb_next;
> + int waiting_ech_skb_num;
> + int xcan_echo_skb_max_tx;
> + int xcan_echo_skb_max_rx;
> + struct napi_struct napi;
> + spinlock_t ech_skb_lock;
> + u32 (*read_reg)(const struct xcan_priv *priv, int reg);
> + void (*write_reg)(const struct xcan_priv *priv, int reg, u32 val);
Why do you have {read,write}_reg function here?
> + struct net_device *dev;
> + void __iomem *reg_base;
> + unsigned long irq_flags;
> + struct clk *aperclk;
> + struct clk *devclk;
> +};
> +
> +/* CAN Bittiming constants as per Xilinx CAN specs */
> +static struct can_bittiming_const xcan_bittiming_const = {
> + .name = DRIVER_NAME,
> + .tseg1_min = 1,
> + .tseg1_max = 16,
> + .tseg2_min = 1,
> + .tseg2_max = 8,
> + .sjw_max = 4,
> + .brp_min = 1,
> + .brp_max = 256,
> + .brp_inc = 1,
> +};
> +
> +/**
> + * xcan_write_reg - Write a value to the device register
> + * @priv: Driver private data structure
> + * @reg: Register offset
> + * @val: Value to write at the Register offset
> + *
> + * Write data to the paricular CAN register
> + */
> +static void xcan_write_reg(const struct xcan_priv *priv, int reg, u32 val)
> +{
> + writel(val, priv->reg_base + reg);
> +}
> +
> +/**
> + * xcan_read_reg - Read a value from the device register
> + * @priv: Driver private data structure
> + * @reg: Register offset
> + *
> + * Read data from the particular CAN register
> + * Return: value read from the CAN register
> + */
> +static u32 xcan_read_reg(const struct xcan_priv *priv, int reg)
> +{
> + return readl(priv->reg_base + reg);
> +}
> +
> +/**
> + * set_reset_mode - Resets the CAN device mode
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver reset mode routine.The driver
> + * enters into configuration mode.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int set_reset_mode(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + unsigned long timeout;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_OFFSET);
> +
> + timeout = jiffies + XCAN_TIMEOUT;
> + while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) {
> + if (time_after(jiffies, timeout)) {
> + netdev_warn(ndev, "timedout waiting for config mode\n");
> + return -ETIMEDOUT;
> + }
> + schedule_timeout(1);
better use usleep_range()
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_set_bittiming - CAN set bit timing routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver set bittiming routine.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_set_bittiming(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + u32 btr0, btr1;
> + u32 is_config_mode;
> +
> + /* Check whether Xilinx CAN is in configuration mode.
> + * It cannot set bit timing if Xilinx CAN is not in configuration mode.
> + */
> + is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) &
> + XCAN_SR_CONFIG_MASK;
> + if (!is_config_mode) {
> + netdev_alert(ndev,
> + "Cannot set bittiming can is not in config mode\n");
> + return -EPERM;
> + }
> +
> + netdev_dbg(ndev, "brp=%d,prop=%d,phase_seg1:%d,phase_reg2=%d,sjw=%d\n",
> + bt->brp, bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
> + bt->sjw);
I think this dbg can be removed, as it just prints the userspace values.
> +
> + /* Setting Baud Rate prescalar value in BRPR Register */
> + btr0 = (bt->brp - 1) & XCAN_BRPR_BRP_MASK;
> +
> + /* Setting Time Segment 1 in BTR Register */
> + btr1 = (bt->prop_seg + bt->phase_seg1 - 1) & XCAN_BTR_TS1_MASK;
> +
> + /* Setting Time Segment 2 in BTR Register */
> + btr1 |= ((bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT) &
> + XCAN_BTR_TS2_MASK;
> +
> + /* Setting Synchronous jump width in BTR Register */
> + btr1 |= ((bt->sjw - 1) << XCAN_BTR_SJW_SHIFT) & XCAN_BTR_SJW_MASK;
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> + netdev_info(ndev, "Doesn't support Triple Sampling\n");
no need to check, it's not passed to the driver until you advertise you
support it (see priv->can.ctrlmode_supported).
> + netdev_dbg(ndev, "Setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
> +
> + priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
> + priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
> +
> + netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
> + priv->read_reg(priv, XCAN_BRPR_OFFSET),
> + priv->read_reg(priv, XCAN_BTR_OFFSET));
One of the dbg should be enough.
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_start - This the drivers start routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the drivers start routine.
> + * Based on the State of the CAN device it puts
> + * the CAN device into a proper mode.
> + *
> + * Return: 0 always
> + */
> +static int xcan_start(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + /* Check if it is in reset mode */
> + if (priv->can.state != CAN_STATE_STOPPED)
> + set_reset_mode(ndev);
Please check return value of set_reset_mode
> +
> + /* Enable interrupts */
> + priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
> +
> + /* Check whether it is loopback mode or normal mode */
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
> + /* Put device into loopback mode */
> + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_LBACK_MASK);
> + else
> + /* The device is in normal mode */
> + priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
> +
> + if (priv->can.state == CAN_STATE_STOPPED) {
I think your driver is always in CAN_STATE_STOPPED, right?
> + /* Enable Xilinx CAN */
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
> + while ((priv->read_reg(priv, XCAN_SR_OFFSET) &
> + XCAN_SR_LBACK_MASK) == 0)
> + ;
> + } else {
> + while ((priv->read_reg(priv, XCAN_SR_OFFSET)
> + & XCAN_SR_NORMAL_MASK) == 0)
> + ;
Please add a timeout to the loops.
> + }
> + netdev_dbg(ndev, "status:#x%08x\n",
> + priv->read_reg(priv, XCAN_SR_OFFSET));
> + }
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + return 0;
> +}
> +
> +/**
> + * xcan_do_set_mode - This sets the mode of the driver
> + * @ndev: Pointer to net_device structure
> + * @mode: Tells the mode of the driver
> + *
> + * This check the drivers state and calls the
> + * the corresponding modes to set.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int ret;
> +
> + netdev_dbg(ndev, "Setting the mode of the driver%s\n", __func__);
please remove dbg
> +
> + if (!priv->open_time)
> + return -EINVAL;
please remove open_time completely.
> +
> + switch (mode) {
> + case CAN_MODE_START:
> + ret = xcan_start(ndev);
> + if (ret < 0)
> + netdev_err(ndev, "xcan_start failed!\n");
> +
> + if (netif_queue_stopped(ndev))
> + netif_wake_queue(ndev);
just call wake_queue
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * xcan_start_xmit - Starts the transmission
> + * @skb: sk_buff pointer that contains data to be Txed
> + * @ndev: Pointer to net_device structure
> + *
> + * This function is invoked from upper layers to initiate transmission. This
> + * function uses the next available free txbuff and populates their fields to
> + * start the transmission.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + u32 id, dlc, tmp_dw1, tmp_dw2 = 0, data1, data2 = 0;
> + unsigned long flags;
> +
Please add here:
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;
> + /* Check if the TX buffer is full */
> + if (priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_TXFLL_MASK) {
> + netif_stop_queue(ndev);
> + netdev_err(ndev, "TX register is still full!\n");
> + return NETDEV_TX_BUSY;
> + } else if (priv->waiting_ech_skb_num == priv->xcan_echo_skb_max_tx) {
> + netif_stop_queue(ndev);
> + netdev_err(ndev, "waiting:0x%08x, max:0x%08x\n",
> + priv->waiting_ech_skb_num, priv->xcan_echo_skb_max_tx);
> + return NETDEV_TX_BUSY;
> + }
You should handle flow control after you put the CAN frame into the
hardware, but before activating the TX complete interrutp.
> + /* Watch carefully on the bit sequence */
> + if ((cf->can_id & CAN_EFF_FLAG) == 0) {
Nitpick easier to read is:
if (cf->can_id & CAN_EFF_FLAG) {
/* EFF handling */
} else {
/* STD handling */
}
> + /* Standard CAN ID format */
> + id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
> + XCAN_IDR_ID1_MASK;
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + /* Extended frames remote TX request */
> + id |= XCAN_IDR_SRR_MASK;
> + } else {
> + /* Extended CAN ID format */
> + id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
> + XCAN_IDR_ID2_MASK;
> + id |= (((cf->can_id & CAN_EFF_MASK) >>
> + (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
> + XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
> +
> + /* The substibute remote TX request bit should be "1"
> + * for extended frames as in the Xilinx CAN datasheet
> + */
> + id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + /* Extended frames remote TX request */
> + id |= XCAN_IDR_RTR_MASK;
> + }
> +
> + dlc = (cf->can_dlc & 0xf) << XCAN_DLCR_DLC_SHIFT;
With the above check can_dlc is valid.
> +
> + tmp_dw1 = le32_to_cpup((u32 *)(cf->data));
> + data1 = htonl(tmp_dw1);
This looks broken. cf->data is in big endian, what is the endianess of
your registers?
> + if (dlc > 4) {
> + tmp_dw2 = le32_to_cpup((u32 *)(cf->data + 4));
> + data2 = htonl(tmp_dw2);
> + }
> +
> + netdev_dbg(ndev, "tx:id=0x%08x,dlc=0x%08x,d1=0x%08x,d2=0x%08x\n",
> + id, dlc, data1, data2);
please remove the dbg
> + /* Write the Frame to Xilinx CAN TX FIFO */
> + priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id);
> + priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc);
> + priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data1);
> + priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data2);
Which write triggers the transmission?
> + stats->tx_bytes += cf->can_dlc;
Can you move the tx_bytes += to your tx-complete routine?
> + ndev->trans_start = jiffies;
Please remove
> +
> + can_put_echo_skb(skb, ndev, priv->ech_skb_next);
This looks racy, first fill the echo_skb, then start the transmission.
> +
> + priv->ech_skb_next = (priv->ech_skb_next + 1) %
> + priv->xcan_echo_skb_max_tx;
> +
> + spin_lock_irqsave(&priv->ech_skb_lock, flags);
> + priv->waiting_ech_skb_num++;
> + spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
> +
Please move the flow controll handling here.
> + return NETDEV_TX_OK;
> +}
> +
> +/**
> + * xcan_rx - Is called from CAN isr to complete the received
> + * frame processing
> + * @ndev: Pointer to net_device structure
> + *
> + * This function is invoked from the CAN isr(poll) to process the Rx frames. It
> + * does minimal processing and invokes "netif_receive_skb" to complete further
> + * processing.
> + * Return: 0 on success and negative error value on error
> + */
> +static int xcan_rx(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + u32 id_xcan, dlc, data1, data2;
> +
> + skb = alloc_can_skb(ndev, &cf);
> + if (!skb)
> + return -ENOMEM;
> +
> + /* Read a frame from Xilinx zynq CANPS */
> + id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET);
> + dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) & XCAN_DLCR_DLC_MASK;
> + data1 = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
> + data2 = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
If you don't use data? below, don't read them in the first place. Better
move the read below, where you fill the data of the can_frame.
> + netdev_dbg(ndev, "rx:id=0x%08x,dlc=0x%08x,d1=0x%08x,d2=0x%08x\n",
> + id_xcan, dlc, data1, data2);
>
please remove dbg
+
> + /* Change Xilinx CAN data length format to socketCAN data format */
> + cf->can_dlc = get_can_dlc((dlc & XCAN_DLCR_DLC_MASK) >>
> + XCAN_DLCR_DLC_SHIFT);
> +
> + /* Change Xilinx CAN ID format to socketCAN ID format */
> + if (id_xcan & XCAN_IDR_IDE_MASK) {
> + /* The received frame is an Extended format frame */
> + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
> + cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
> + XCAN_IDR_ID2_SHIFT;
> + cf->can_id |= CAN_EFF_FLAG;
> + if (id_xcan & XCAN_IDR_RTR_MASK)
> + cf->can_id |= CAN_RTR_FLAG;
> + } else {
> + /* The received frame is a standard format frame */
> + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
> + XCAN_IDR_ID1_SHIFT;
> + if (id_xcan & XCAN_IDR_RTR_MASK)
> + cf->can_id |= CAN_RTR_FLAG;
> + }
> +
> + /* Change Xilinx CAN data format to socketCAN data format */
Don't fill cf->data if RTR is set. The endianess handling looks weird
here, too.
> + *(u32 *)(cf->data) = ntohl(data1);
> + if (cf->can_dlc > 4)
> + *(u32 *)(cf->data + 4) = ntohl(data2);
> + else
> + *(u32 *)(cf->data + 4) = 0;
no need to set to zero
> + stats->rx_bytes += cf->can_dlc;
please group rx_bytes and rx_packets handling
> +
> + can_led_event(ndev, CAN_LED_EVENT_RX);
> +
> + netif_receive_skb(skb);
> +
> + stats->rx_packets++;
> + return 0;
> +}
> +
> +/**
> + * xcan_err_interrupt - error frame Isr
> + * @ndev: net_device pointer
> + * @isr: interrupt status register value
> + *
> + * This is the CAN error interrupt and it will
> + * check the the type of error and forward the error
> + * frame to upper layers.
> + */
> +static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + u32 err_status, status;
> +
> + skb = alloc_can_err_skb(ndev, &cf);
> + if (!skb) {
> + netdev_err(ndev, "alloc_can_err_skb() failed!\n");
> + return;
> + }
> +
> + err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
> + priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
> + status = priv->read_reg(priv, XCAN_SR_OFFSET);
> +
> + if (isr & XCAN_IXR_BSOFF_MASK) {
> + priv->can.state = CAN_STATE_BUS_OFF;
> + cf->can_id |= CAN_ERR_BUSOFF;
> + priv->can.can_stats.bus_off++;
> + /* Leave device in Config Mode in bus-off state */
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + can_bus_off(ndev);
> + } else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
> + priv->can.can_stats.error_passive++;
> + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE |
> + CAN_ERR_CRTL_TX_PASSIVE;
> + } else if (status & XCAN_SR_ERRWRN_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + priv->can.state = CAN_STATE_ERROR_WARNING;
> + priv->can.can_stats.error_warning++;
> + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING |
> + CAN_ERR_CRTL_TX_WARNING;
> + }
> +
> + /* Check for Arbitration lost interrupt */
> + if (isr & XCAN_IXR_ARBLST_MASK) {
> + cf->can_id |= CAN_ERR_LOSTARB;
> + cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
> + priv->can.can_stats.arbitration_lost++;
> + }
> +
> + /* Check for RX FIFO Overflow interrupt */
> + if (isr & XCAN_IXR_RXOFLW_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> + stats->rx_over_errors++;
> + stats->rx_errors++;
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + }
> +
> + /* Check for error interrupt */
> + if (isr & XCAN_IXR_ERROR_MASK) {
> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> + cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +
> + /* Check for Ack error interrupt */
> + if (err_status & XCAN_ESR_ACKER_MASK) {
> + cf->can_id |= CAN_ERR_ACK;
> + cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
> + stats->tx_errors++;
> + }
> +
> + /* Check for Bit error interrupt */
> + if (err_status & XCAN_ESR_BERR_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_BIT;
> + stats->tx_errors++;
> + }
> +
> + /* Check for Stuff error interrupt */
> + if (err_status & XCAN_ESR_STER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_STUFF;
> + stats->rx_errors++;
> + }
> +
> + /* Check for Form error interrupt */
> + if (err_status & XCAN_ESR_FMER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_FORM;
> + stats->rx_errors++;
> + }
> +
> + /* Check for CRC error interrupt */
> + if (err_status & XCAN_ESR_CRCER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL;
> + stats->rx_errors++;
> + }
> + priv->can.can_stats.bus_error++;
> + }
> +
> + netif_rx(skb);
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +
> + netdev_dbg(ndev, "%s: error status register:0x%x\n",
> + __func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
> +}
> +
> +/**
> + * xcan_state_interrupt - It will check the state of the CAN device
> + * @ndev: net_device pointer
> + * @isr: interrupt status register value
> + *
> + * This will checks the state of the CAN device
> + * and puts the device into appropriate state.
> + */
> +static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + /* Check for Sleep interrupt if set put CAN device in sleep state */
> + if (isr & XCAN_IXR_SLP_MASK)
> + priv->can.state = CAN_STATE_SLEEPING;
> +
> + /* Check for Wake up interrupt if set put CAN device in Active state */
> + if (isr & XCAN_IXR_WKUP_MASK)
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +}
> +
> +/**
> + * xcan_rx_poll - Poll routine for rx packets (NAPI)
> + * @napi: napi structure pointer
> + * @quota: Max number of rx packets to be processed.
> + *
> + * This is the poll routine for rx part.
> + * It will process the packets maximux quota value.
> + *
> + * Return: number of packets received
> + */
> +static int xcan_rx_poll(struct napi_struct *napi, int quota)
> +{
> + struct net_device *ndev = napi->dev;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 isr, ier;
> + int work_done = 0;
> +
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
> + if (isr & XCAN_IXR_RXOK_MASK) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET,
> + XCAN_IXR_RXOK_MASK);
> + if (xcan_rx(ndev) < 0)
> + return work_done;
> + work_done++;
> + } else {
> + priv->write_reg(priv, XCAN_ICR_OFFSET,
> + XCAN_IXR_RXNEMP_MASK);
> + break;
> + }
> + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + }
> +
> + if (work_done < quota) {
> + napi_complete(napi);
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK);
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + }
> + return work_done;
> +}
> +
> +/**
> + * xcan_tx_interrupt - Tx Done Isr
> + * @ndev: net_device pointer
> + */
> +static void xcan_tx_interrupt(struct net_device *ndev)
> +{
> + unsigned long flags;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + u32 processed = 0, txpackets;
> +
> + stats->tx_packets++;
> + netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
> + priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
> +
> + txpackets = priv->waiting_ech_skb_num;
> +
> + if (txpackets) {
> + can_get_echo_skb(ndev, priv->waiting_ech_skb_index);
> + priv->waiting_ech_skb_index =
> + (priv->waiting_ech_skb_index + 1) %
> + priv->xcan_echo_skb_max_tx;
> + processed++;
> + txpackets--;
> + }
> +
> + spin_lock_irqsave(&priv->ech_skb_lock, flags);
> + priv->waiting_ech_skb_num -= processed;
> + spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
> +
> + netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
> + priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
> +
> + netif_wake_queue(ndev);
> +
> + can_led_event(ndev, CAN_LED_EVENT_TX);
> +}
> +
> +/**
> + * xcan_interrupt - CAN Isr
> + * @irq: irq number
> + * @dev_id: device id poniter
> + *
> + * This is the xilinx CAN Isr. It checks for the type of interrupt
> + * and invokes the corresponding ISR.
> + *
> + * Return:
> + * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
> + */
> +static irqreturn_t xcan_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *)dev_id;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 isr, ier;
> +
> + if (priv->can.state == CAN_STATE_STOPPED)
This should not happen, please remove.
> + return IRQ_NONE;
> +
> + /* Get the interrupt status from Xilinx CAN */
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + if (!isr)
> + return IRQ_NONE;
> +
> + netdev_dbg(ndev, "%s: isr:#x%08x, err:#x%08x\n", __func__,
> + isr, priv->read_reg(priv, XCAN_ESR_OFFSET));
> +
> + /* Check for the type of interrupt and Processing it */
> + if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK |
> + XCAN_IXR_WKUP_MASK));
> + xcan_state_interrupt(ndev, isr);
> + }
> +
> + /* Check for Tx interrupt and Processing it */
> + if (isr & XCAN_IXR_TXOK_MASK) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
> + xcan_tx_interrupt(ndev);
> + }
> +
> + /* Check for the type of error interrupt and Processing it */
> + if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
> + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK |
> + XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK |
> + XCAN_IXR_ARBLST_MASK));
> + xcan_err_interrupt(ndev, isr);
> + }
> +
> + /* Check for the type of receive interrupt and Processing it */
> + if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) {
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK);
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + napi_schedule(&priv->napi);
> + }
> + return IRQ_HANDLED;
> +}
> +
> +/**
> + * xcan_stop - Driver stop routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the drivers stop routine. It will disable the
> + * interrupts and put the device into configuration mode.
> + */
> +static void xcan_stop(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 ier;
> +
> + /* Disable interrupts and leave the can in configuration mode */
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier &= ~XCAN_INTR_ALL;
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + priv->can.state = CAN_STATE_STOPPED;
> +}
> +
> +/**
> + * xcan_open - Driver open routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver open routine.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_open(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int err;
> +
> + /* Set chip into reset mode */
> + err = set_reset_mode(ndev);
> + if (err < 0)
> + netdev_err(ndev, "mode resetting failed failed!\n");
> +
> + /* Common open */
> + err = open_candev(ndev);
> + if (err)
> + return err;
> +
> + err = xcan_start(ndev);
> + if (err < 0)
> + netdev_err(ndev, "xcan_start failed!\n");
> +
> +
> + can_led_event(ndev, CAN_LED_EVENT_OPEN);
> + napi_enable(&priv->napi);
> + netif_start_queue(ndev);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_close - Driver close routine
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 always
> + */
> +static int xcan_close(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + netif_stop_queue(ndev);
> + napi_disable(&priv->napi);
> + xcan_stop(ndev);
> + close_candev(ndev);
> +
> + can_led_event(ndev, CAN_LED_EVENT_STOP);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_get_berr_counter - error counter routine
> + * @ndev: Pointer to net_device structure
> + * @bec: Pointer to can_berr_counter structure
> + *
> + * This is the driver error counter routine.
> + * Return: 0 always
> + */
> +static int xcan_get_berr_counter(const struct net_device *ndev,
> + struct can_berr_counter *bec)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
> + bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
> + XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
> + return 0;
> +}
> +
> +static const struct net_device_ops xcan_netdev_ops = {
> + .ndo_open = xcan_open,
> + .ndo_stop = xcan_close,
> + .ndo_start_xmit = xcan_start_xmit,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +/**
> + * xcan_suspend - Suspend method for the driver
> + * @_dev: Address of the platform_device structure
> + *
> + * Put the driver into low power mode.
> + * Return: 0 always
> + */
> +static int xcan_suspend(struct device *_dev)
> +{
> + struct platform_device *pdev = container_of(_dev,
> + struct platform_device, dev);
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + if (netif_running(ndev)) {
> + netif_stop_queue(ndev);
> + netif_device_detach(ndev);
> + }
> +
> + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
> + priv->can.state = CAN_STATE_SLEEPING;
> +
> + clk_disable(priv->aperclk);
> + clk_disable(priv->devclk);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_resume - Resume from suspend
> + * @dev: Address of the platformdevice structure
> + *
> + * Resume operation after suspend.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_resume(struct device *dev)
> +{
> + struct platform_device *pdev = container_of(dev,
> + struct platform_device, dev);
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int ret;
> +
> + ret = clk_enable(priv->aperclk);
> + if (ret) {
> + dev_err(dev, "Cannot enable clock.\n");
> + return ret;
> + }
> + ret = clk_enable(priv->devclk);
> + if (ret) {
> + dev_err(dev, "Cannot enable clock.\n");
> + return ret;
> + }
> +
> + priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> + if (netif_running(ndev)) {
> + netif_device_attach(ndev);
> + netif_start_queue(ndev);
> + }
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
> +
> +/**
> + * xcan_probe - Platform registration call
> + * @pdev: Handle to the platform device structure
> + *
> + * This function does all the memory allocation and registration for the CAN
> + * device.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_probe(struct platform_device *pdev)
> +{
> + struct resource *res; /* IO mem resources */
> + struct net_device *ndev;
> + struct xcan_priv *priv;
> + int ret, fifodep;
> +
> + /* Create a CAN device instance */
> + ndev = alloc_candev(sizeof(struct xcan_priv), XCAN_ECHO_SKB_MAX);
> + if (!ndev)
> + return -ENOMEM;
> +
> + priv = netdev_priv(ndev);
> + priv->dev = ndev;
> + priv->can.bittiming_const = &xcan_bittiming_const;
> + priv->can.do_set_bittiming = xcan_set_bittiming;
> + priv->can.do_set_mode = xcan_do_set_mode;
> + priv->can.do_get_berr_counter = xcan_get_berr_counter;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
> + CAN_CTRLMODE_BERR_REPORTING;
> + priv->xcan_echo_skb_max_tx = XCAN_ECHO_SKB_MAX;
> + priv->xcan_echo_skb_max_rx = XCAN_NAPI_WEIGHT;
> +
> + /* Get IRQ for the device */
> + ndev->irq = platform_get_irq(pdev, 0);
> + ret = devm_request_irq(&pdev->dev, ndev->irq, &xcan_interrupt,
> + priv->irq_flags, dev_name(&pdev->dev),
> + (void *)ndev);
We usually request the interrupt on in the open() function
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Irq allocation for CAN failed\n");
> + goto err_free;
> + }
> +
> + spin_lock_init(&priv->ech_skb_lock);
> + ndev->flags |= IFF_ECHO; /* We support local echo */
> +
> + platform_set_drvdata(pdev, ndev);
> + SET_NETDEV_DEV(ndev, &pdev->dev);
> + ndev->netdev_ops = &xcan_netdev_ops;
> +
> + /* Get the virtual base address for the device */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->reg_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->reg_base)) {
> + ret = PTR_ERR(priv->reg_base);
> + goto err_free;
> + }
> + ndev->mem_start = res->start;
> + ndev->mem_end = res->end;
> +
> + priv->write_reg = xcan_write_reg;
> + priv->read_reg = xcan_read_reg;
> +
> + /* Getting the CAN devclk info */
> + priv->devclk = devm_clk_get(&pdev->dev, "ref_clk");
> + if (IS_ERR(priv->devclk)) {
> + dev_err(&pdev->dev, "Device clock not found.\n");
> + ret = PTR_ERR(priv->devclk);
> + goto err_free;
> + }
> +
> + /* Check for type of CAN device */
> + if (of_device_is_compatible(pdev->dev.of_node,
> + "xlnx,zynq-can-1.00.a")) {
> + priv->aperclk = devm_clk_get(&pdev->dev, "aper_clk");
> + if (IS_ERR(priv->aperclk)) {
> + dev_err(&pdev->dev, "aper clock not found\n");
> + ret = PTR_ERR(priv->aperclk);
> + goto err_free;
> + }
> + } else {
> + priv->aperclk = priv->devclk;
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "xlnx,can-tx-dpth", &fifodep);
> + if (ret < 0)
> + goto err_free;
> + priv->xcan_echo_skb_max_tx = fifodep;
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "xlnx,can-rx-dpth", &fifodep);
> + if (ret < 0)
> + goto err_free;
> + priv->xcan_echo_skb_max_rx = fifodep;
> + }
> +
> + ret = clk_prepare_enable(priv->devclk);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable device clock\n");
> + goto err_free;
> + }
> +
> + ret = clk_prepare_enable(priv->aperclk);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable aper clock\n");
> + goto err_unprepar_disabledev;
> + }
> +
> + priv->can.clock.freq = clk_get_rate(priv->devclk);
> +
> + netif_napi_add(ndev, &priv->napi, xcan_rx_poll,
> + priv->xcan_echo_skb_max_rx);
> + ret = register_candev(ndev);
> + if (ret) {
> + dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
> + goto err_unprepar_disableaper;
> + }
> +
> + devm_can_led_init(ndev);
> + dev_info(&pdev->dev,
> + "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
> + priv->reg_base, ndev->irq, priv->can.clock.freq,
> + priv->xcan_echo_skb_max_tx);
> +
> + return 0;
> +
> +err_unprepar_disableaper:
> + clk_disable_unprepare(priv->aperclk);
> +err_unprepar_disabledev:
> + clk_disable_unprepare(priv->devclk);
> +err_free:
> + free_candev(ndev);
> +
> + return ret;
> +}
> +
> +/**
> + * xcan_remove - Unregister the device after releasing the resources
> + * @pdev: Handle to the platform device structure
> + *
> + * This function frees all the resources allocated to the device.
> + * Return: 0 always
> + */
> +static int xcan_remove(struct platform_device *pdev)
> +{
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + if (set_reset_mode(ndev) < 0)
> + netdev_err(ndev, "mode resetting failed!\n");
> +
> + unregister_candev(ndev);
> + netif_napi_del(&priv->napi);
> + clk_disable_unprepare(priv->aperclk);
> + clk_disable_unprepare(priv->devclk);
> +
> + free_candev(ndev);
> +
> + return 0;
> +}
> +
> +/* Match table for OF platform binding */
> +static struct of_device_id xcan_of_match[] = {
> + { .compatible = "xlnx,zynq-can-1.00.a", },
> + { .compatible = "xlnx,axi-can-1.00.a", },
> + { /* end of list */ },
> +};
> +MODULE_DEVICE_TABLE(of, xcan_of_match);
> +
> +static struct platform_driver xcan_driver = {
> + .probe = xcan_probe,
> + .remove = xcan_remove,
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = DRIVER_NAME,
> + .pm = &xcan_dev_pm_ops,
> + .of_match_table = xcan_of_match,
> + },
> +};
> +
> +module_platform_driver(xcan_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Xilinx Inc");
> +MODULE_DESCRIPTION("Xilinx CAN interface");
>
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 242 bytes --]
^ permalink raw reply
* Re: [alsa-devel] [PATCH] ASoC: pcm512x: Add PCM512x driver
From: Takashi Iwai @ 2014-02-06 12:51 UTC (permalink / raw)
To: Mark Brown
Cc: Florian Meier, Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
linaro-kernel-cunTk1MwBs8s++Sfvej+rw, Mark Brown
In-Reply-To: <1391689575-4039-1-git-send-email-broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
At Thu, 6 Feb 2014 12:26:15 +0000,
Mark Brown wrote:
> diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
(snip)
> +static const char *pcm512x_dsp_program_texts[] = {
> + "FIR interpolation with de-emphasis",
> + "Low latency IIR with de-emphasis",
> + "High attenuation with de-emphasis",
> + "Ringing-less low latency FIR",
> +};
> +
> +static const unsigned int pcm512x_dsp_program_values[] = {
> + 1,
> + 2,
> + 3,
> + 5,
> + 7,
> +};
The numbers of items in pcm512x_dsp_program_texts[] and _values[]
don't match.
> +static const SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
> + PCM512x_DSP_PROGRAM, 0, 0x1f,
> + pcm512x_dsp_program_texts,
> + pcm512x_dsp_program_values);
> +
> +static const char *pcm512x_clk_missing_text[] = {
> + "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
> +};
> +
> +static const struct soc_enum pcm512x_clk_missing =
> + SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 7, pcm512x_clk_missing_text);
Isn't it 8?
> +
> +static const char *pcm512x_autom_text[] = {
> + "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
> +};
> +
> +static const struct soc_enum pcm512x_autom_l =
> + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 7,
> + pcm512x_autom_text);
> +
> +static const struct soc_enum pcm512x_autom_r =
> + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 7,
> + pcm512x_autom_text);
Ditto.
Takashi
--
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: [alsa-devel] [PATCH] ASoC: pcm512x: Add PCM512x driver
From: Mark Brown @ 2014-02-06 13:07 UTC (permalink / raw)
To: Takashi Iwai
Cc: Florian Meier, Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
linaro-kernel-cunTk1MwBs8s++Sfvej+rw
In-Reply-To: <s5hr47gv1yi.wl%tiwai-l3A5Bk7waGM@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 669 bytes --]
On Thu, Feb 06, 2014 at 01:51:33PM +0100, Takashi Iwai wrote:
> Mark Brown wrote:
> > +static const char *pcm512x_dsp_program_texts[] = {
> The numbers of items in pcm512x_dsp_program_texts[] and _values[]
> don't match.
Yeah, fixed.
> > +static const struct soc_enum pcm512x_clk_missing =
> > + SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 7, pcm512x_clk_missing_text);
> Isn't it 8?
Hrm, it is but this points out an error in the control helpers which has
been there since forever - they call that parameter max but it's not a
maximum, it's the number of elements in the enumeration. I bet we have
a bunch of other enumerations which miss the last element as a result.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ 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