All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jeppe Ledet-Pedersen <jlp@gomspace.com>
To: <lee.jones@linaro.org>, <arnd@arndb.de>,
	<gregkh@linuxfoundation.org>,
	<alexandre.belloni@free-electrons.com>, <a.zummo@towertech.it>,
	<robh+dt@kernel.org>, <pawel.moll@arm.com>,
	<mark.rutland@arm.com>, <ijc+devicetree@hellion.org.uk>,
	<galak@codeaurora.org>
Cc: <rtc-linux@googlegroups.com>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	Jeppe Ledet-Pedersen <jlp@gomspace.com>
Subject: [PATCH 1/3] mfd: add Cypress FM33256B Processor Companion driver
Date: Wed, 20 Apr 2016 13:07:49 +0200	[thread overview]
Message-ID: <1461150471-23163-2-git-send-email-jlp@gomspace.com> (raw)
In-Reply-To: <1461150471-23163-1-git-send-email-jlp@gomspace.com>

This patch adds support for the Cypress Semiconductor FM33256B processor
companion. The device contains a 256 kbit FRAM, an RTC, a supply voltage
monitor, and a watchdog timer.

Signed-off-by: Jeppe Ledet-Pedersen <jlp@gomspace.com>
---
 Documentation/devicetree/bindings/mfd/fm33256b.txt |  30 ++
 MAINTAINERS                                        |   6 +
 drivers/mfd/Kconfig                                |  16 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/fm33256b.c                             | 488 +++++++++++++++++++++
 include/linux/mfd/fm33256b.h                       |  76 ++++
 6 files changed, 617 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/fm33256b.txt
 create mode 100644 drivers/mfd/fm33256b.c
 create mode 100644 include/linux/mfd/fm33256b.h

diff --git a/Documentation/devicetree/bindings/mfd/fm33256b.txt b/Documentation/devicetree/bindings/mfd/fm33256b.txt
new file mode 100644
index 0000000..6591c94
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/fm33256b.txt
@@ -0,0 +1,30 @@
+Device-tree bindings for Cypress Semiconductor FM33256B Processor Companion
+---------------------------------------------------------------------------
+
+Required properties:
+- compatible: must be "cypress,fm33256b".
+- reg: SPI chip select
+- spi-max-frequency: Max SPI frequency to use (< 16000000)
+
+Optional properties:
+- cypress,charge-enabled: enable trickle charger
+- cypress,charge-fast: enable fast (1 mA) charging
+
+The MFD exposes two subdevices:
+- The FRAM: "cypress,fm33256b-fram"
+- The RTC: "cypress,fm33256b-rtc"
+
+Example:
+
+spi1: spi@f800800 {
+	status = "okay";
+	cs-gpios = <&pioC 25 0>;
+
+	fm33256b@0 {
+		compatible = "cypress,fm33256b";
+		spi-max-frequency = <10000000>;
+		cypress,charge-enabled;
+		cypress,charge-fast;
+		reg = <0>;
+	};
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 1d5b4be..87b5023 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3382,6 +3382,12 @@ T:	git git://linuxtv.org/anttip/media_tree.git
 S:	Maintained
 F:	drivers/media/common/cypress_firmware*
 
+CYPRESS FM33256B PROCESSOR COMPANION DRIVER
+M:	Jeppe Ledet-Pedersen <jlp@gomspace.com>
+S:	Maintained
+F:	include/linux/mfd/fm33256b.h
+F:	drivers/mfd/fm33256b.c
+
 CYTTSP TOUCHSCREEN DRIVER
 M:	Ferruh Yigit <fery@cypress.com>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index eea61e3..3a0d3a3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -93,6 +93,22 @@ config MFD_ATMEL_HLCDC
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_FM33256B
+	tristate "Cypress FM33256 Processor Companion support"
+	select MFD_CORE
+	select REGMAP_SPI
+	depends on OF
+	help
+	  If you say yes here you get support for the Cypress FM33256B
+	  Processor Companion device.
+
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+	  This driver can also be built as a module. If so the module
+	  will be called fm33256b.
+
 config MFD_BCM590XX
 	tristate "Broadcom BCM590xx PMUs"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5eaa6465d..a13728d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
+obj-$(CONFIG_MFD_FM33256B)	+= fm33256b.o
diff --git a/drivers/mfd/fm33256b.c b/drivers/mfd/fm33256b.c
new file mode 100644
index 0000000..880ccdd
--- /dev/null
+++ b/drivers/mfd/fm33256b.c
@@ -0,0 +1,488 @@
+/*
+ * Cypress FM33256B Processor Companion Driver
+ *
+ * Copyright (C) 2016 GomSpace ApS
+ *
+ * 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.
+ *
+ * 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/mfd/fm33256b.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/of.h>
+
+static const struct mfd_cell fm33256b_cells[] = {
+	{
+		.name = "fm33256b-rtc",
+		.of_compatible = "cypress,fm33256b-rtc",
+	},
+	{
+		.name = "fm33256b-fram",
+		.of_compatible = "cypress,fm33256b-fram",
+	},
+};
+
+static int fm33256b_io(struct spi_device *spi, bool write_enable,
+		       uint8_t *out, uint8_t *in, size_t len)
+{
+	struct spi_message m;
+	struct fm33256b *fm33256b = dev_get_drvdata(&spi->dev);
+
+	uint8_t write_out[1] = {FM33256B_OP_WREN};
+
+	/* Payload transfer */
+	struct spi_transfer t = {
+		.tx_buf = out,
+		.rx_buf = in,
+		.len = len,
+	};
+
+	mutex_lock(&fm33256b->lock);
+
+	/* CS must go high for the write enable latch to be enabled,
+	 * so we have to split this in two transfers.
+	 */
+	if (write_enable)
+		spi_write(spi, write_out, 1);
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	mutex_unlock(&fm33256b->lock);
+
+	return 0;
+}
+
+static int fm33256b_read_status(struct spi_device *spi, uint8_t *status)
+{
+	int ret;
+	uint8_t out[2] = {FM33256B_OP_RDSR, 0xff};
+	uint8_t in[2];
+
+	ret = fm33256b_io(spi, false, out, in, 2);
+	if (ret < 0)
+		return ret;
+
+	*status = in[1];
+
+	return 0;
+}
+
+static int fm33256b_write_status(struct spi_device *spi, uint8_t status)
+{
+	uint8_t out[2] = {FM33256B_OP_WRSR, status};
+	uint8_t in[2];
+
+	return fm33256b_io(spi, true, out, in, 2);
+}
+
+static int fm33256b_write_fram(struct spi_device *spi, uint16_t addr,
+			       const uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_WRITE;
+	out[1] = (addr >> 8) & 0xff;
+	out[2] = addr & 0xff;
+	memcpy(&out[3], data, len);
+
+	ret = fm33256b_io(spi, true, out, in, 3 + len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_read_fram(struct spi_device *spi, uint16_t addr,
+			      uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_READ;
+	out[1] = (addr >> 8) & 0xff;
+	out[2] = addr & 0xff;
+	memset(&out[3], 0xff, len);
+
+	ret = fm33256b_io(spi, false, out, in, 3 + len);
+	if (ret == 0)
+		memcpy(data, &in[3], len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_write_pc(struct spi_device *spi, uint8_t reg,
+			     const uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_WRPC;
+	out[1] = reg;
+	memcpy(&out[2], data, len);
+
+	ret = fm33256b_io(spi, true, out, in, 2 + len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_read_pc(struct spi_device *spi, uint8_t reg,
+			    uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_RDPC;
+	out[1] = reg;
+	memset(&out[2], 0xff, len);
+
+	ret = fm33256b_io(spi, false, out, in, 2 + len);
+	if (ret == 0)
+		memcpy(data, &in[2], len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_pc_regmap_read(void *context, const void *reg,
+				   size_t reg_size, void *val,
+				   size_t val_size)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	if (reg_size != 1)
+		return -ENOTSUPP;
+
+	return fm33256b_read_pc(spi, *(uint8_t *)reg, val, val_size);
+}
+
+static int fm33256b_pc_regmap_write(void *context, const void *data,
+				    size_t count)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *out = data;
+	const uint8_t *val = &out[1];
+
+	uint8_t reg = out[0];
+
+	return fm33256b_write_pc(spi, reg, val, count - sizeof(reg));
+}
+
+static int fm33256b_fram_regmap_read(void *context, const void *reg,
+				     size_t reg_size, void *val,
+				     size_t val_size)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *addrp = reg;
+
+	uint16_t addr = ((uint16_t)addrp[0] << 8) | addrp[1];
+
+	if (reg_size != 2)
+		return -ENOTSUPP;
+
+	return fm33256b_read_fram(spi, addr, val, val_size);
+}
+
+static int fm33256b_fram_regmap_write(void *context, const void *data,
+				      size_t count)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *out = data;
+	const uint8_t *val = &out[2];
+
+	uint16_t addr = ((uint16_t)out[0] << 8) | out[1];
+
+	return fm33256b_write_fram(spi, addr, val, count - sizeof(addr));
+}
+
+static ssize_t fm33256b_bp_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+	uint8_t status, bp;
+
+	ret = fm33256b_read_status(spi, &status);
+	if (ret < 0)
+		return ret;
+
+	bp = (status & 0x0c) >> 2;
+
+	return snprintf(buf, PAGE_SIZE, "%hhu\n", bp);
+}
+
+static ssize_t fm33256b_bp_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+	uint8_t status, bp;
+	unsigned long input;
+
+	ret = kstrtoul(buf, 10, &input);
+	if (ret < 0)
+		return ret;
+
+	if (input > 3)
+		return -EINVAL;
+
+	bp = (uint8_t)input << 2;
+
+	ret = fm33256b_read_status(spi, &status);
+	if (ret < 0)
+		return ret;
+
+	status = (status & 0xf3) | bp;
+
+	ret = fm33256b_write_status(spi, status);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t fm33256b_serial_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	char serial[9];
+	int ret;
+
+	ret = fm33256b_read_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8);
+	if (ret < 0)
+		return ret;
+
+	serial[8] = '\0';
+
+	return snprintf(buf, PAGE_SIZE, "%-8s\n", serial);
+}
+
+static ssize_t fm33256b_serial_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	char serial[9];
+	int ret;
+
+	if (sscanf(buf, "%8s", serial) != 1)
+		return -EINVAL;
+
+	ret = fm33256b_write_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+DEVICE_ATTR(bp, S_IWUSR | S_IRUGO,
+	    fm33256b_bp_show, fm33256b_bp_store);
+DEVICE_ATTR(serial, S_IWUSR | S_IRUGO,
+	    fm33256b_serial_show, fm33256b_serial_store);
+
+/* Processor Companion Register Map */
+static const struct regmap_config fm33256b_pc_regmap_conf = {
+	.name = "pc",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = FM33256B_MAX_REGISTER,
+};
+
+static struct regmap_bus fm33256b_pc_regmap_bus = {
+	.write = fm33256b_pc_regmap_write,
+	.read = fm33256b_pc_regmap_read,
+};
+
+/* FRAM Register Map */
+static const struct regmap_config fm33256b_fram_regmap_conf = {
+	.name = "fram",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = FM33256B_MAX_FRAM,
+};
+
+static struct regmap_bus fm33256b_fram_regmap_bus = {
+	.write = fm33256b_fram_regmap_write,
+	.read = fm33256b_fram_regmap_read,
+};
+
+static int fm33256b_setup(struct spi_device *spi, struct fm33256b *fm33256b)
+{
+	int ret;
+	uint8_t companion_ctl = FM33256B_ALSW, rtc_alarm_ctl = 0;
+
+	/* Setup charger control from DT */
+	if (of_get_property(spi->dev.of_node, "cypress,charge-enabled", NULL))
+		companion_ctl |= FM33256B_VBC;
+
+	if (of_get_property(spi->dev.of_node, "cypress,charge-fast", NULL))
+		companion_ctl |= FM33256B_FC;
+
+	/* Setup charging if enabled */
+	ret = regmap_write(fm33256b->regmap_pc,
+			   FM33256B_COMPANION_CONTROL_REG,
+			   companion_ctl);
+	if (ret < 0)
+		return ret;
+
+	/* Enable 32 kHz oscillator */
+	ret = regmap_write(fm33256b->regmap_pc,
+			   FM33256B_RTC_ALARM_CONTROL_REG,
+			   rtc_alarm_ctl);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int fm33256b_probe(struct spi_device *spi)
+{
+	int ret;
+	struct device *dev = &spi->dev;
+	struct fm33256b *fm33256b;
+
+	fm33256b = devm_kzalloc(dev, sizeof(*fm33256b), GFP_KERNEL);
+	if (!fm33256b)
+		return -ENOMEM;
+
+	mutex_init(&fm33256b->lock);
+
+	spi->mode = SPI_MODE_0;
+	spi->max_speed_hz = spi->max_speed_hz ? : 8000000;
+
+	ret = spi_setup(spi);
+	if (ret < 0)
+		return ret;
+
+	/* Setup processor companion regmap */
+	fm33256b->regmap_pc =
+		devm_regmap_init(dev, &fm33256b_pc_regmap_bus,
+				 dev, &fm33256b_pc_regmap_conf);
+	if (IS_ERR(fm33256b->regmap_pc))
+		return PTR_ERR(fm33256b->regmap_pc);
+
+	/* Setup FRAM regmap */
+	fm33256b->regmap_fram =
+		devm_regmap_init(dev, &fm33256b_fram_regmap_bus,
+				 dev, &fm33256b_fram_regmap_conf);
+	if (IS_ERR(fm33256b->regmap_fram))
+		return PTR_ERR(fm33256b->regmap_fram);
+
+	dev_set_drvdata(dev, fm33256b);
+
+	ret = fm33256b_setup(spi, fm33256b);
+	if (ret < 0)
+		return ret;
+
+	/* Create sysfs entries */
+	ret = device_create_file(&spi->dev, &dev_attr_bp);
+	if (ret < 0)
+		return ret;
+
+	ret = device_create_file(&spi->dev, &dev_attr_serial);
+	if (ret < 0) {
+		device_remove_file(&spi->dev, &dev_attr_bp);
+		return ret;
+	}
+
+	return mfd_add_devices(dev, -1, fm33256b_cells,
+			       ARRAY_SIZE(fm33256b_cells),
+			       NULL, 0, NULL);
+}
+
+static int fm33256b_remove(struct spi_device *spi)
+{
+	mfd_remove_devices(&spi->dev);
+	device_remove_file(&spi->dev, &dev_attr_serial);
+	device_remove_file(&spi->dev, &dev_attr_bp);
+
+	return 0;
+}
+
+static const struct of_device_id fm33256b_dt_ids[] = {
+	{ .compatible = "cypress,fm33256b" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, fm33256b_dt_ids);
+
+static struct spi_driver fm33256b_spi_driver = {
+	.driver = {
+		.name = "fm33256b",
+		.owner = THIS_MODULE,
+		.of_match_table = fm33256b_dt_ids,
+	},
+	.probe = fm33256b_probe,
+	.remove = fm33256b_remove,
+};
+module_spi_driver(fm33256b_spi_driver);
+
+MODULE_ALIAS("spi:fm33256b");
+MODULE_DESCRIPTION("Cypress FM33256B Processor Companion Driver");
+MODULE_AUTHOR("Jeppe Ledet-Pedersen <jlp@gomspace.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/fm33256b.h b/include/linux/mfd/fm33256b.h
new file mode 100644
index 0000000..cd1e37a
--- /dev/null
+++ b/include/linux/mfd/fm33256b.h
@@ -0,0 +1,76 @@
+/*
+ * Cypress FM33256B Processor Companion Driver
+ *
+ * Copyright (C) 2016 GomSpace ApS
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_MFD_FM33256B_H
+#define __LINUX_MFD_FM33256B_H
+
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+/* Opcodes */
+#define FM33256B_OP_WREN		0x06
+#define FM33256B_OP_WRDI		0x04
+#define FM33256B_OP_RDSR		0x05
+#define FM33256B_OP_WRSR		0x01
+#define FM33256B_OP_READ		0x03
+#define FM33256B_OP_WRITE		0x02
+#define FM33256B_OP_RDPC		0x13
+#define FM33256B_OP_WRPC		0x12
+
+/* RTC/Processor Companion Register Map */
+#define FM33256B_ALARM_MONTH		0x1D
+#define FM33256B_COMPANION_CONTROL_REG	0x18
+#define FM33256B_SERIAL_BYTE0_REG	0x10
+#define FM33256B_YEARS_REG		0x08
+#define FM33256B_MONTH_REG		0x07
+#define FM33256B_DATE_REG		0x06
+#define FM33256B_DAY_REG		0x05
+#define FM33256B_HOURS_REG		0x04
+#define FM33256B_MINUTES_REG		0x03
+#define FM33256B_SECONDS_REG		0x02
+#define FM33256B_CAL_CONTROL_REG	0x01
+#define FM33256B_RTC_ALARM_CONTROL_REG	0x00
+
+/* Companion Control bits */
+#define FM33256B_ALSW			BIT(6)
+#define FM33256B_VBC			BIT(3)
+#define FM33256B_FC			BIT(2)
+
+/* RTC/Alarm Control bits */
+#define FM33256B_R			BIT(0)
+#define FM33256B_W			BIT(1)
+#define FM33256B_CAL			BIT(2)
+#define FM33256B_OSCEN			BIT(7)
+
+/* Limits */
+#define FM33256B_MAX_REGISTER		FM33256B_ALARM_MONTH
+#define FM33256B_MAX_FRAM		(32 * 1024) /* 256 kb */
+
+/**
+ * Structure shared by the MFD device and its subdevices.
+ *
+ * @regmap: register map used to access registers
+ */
+struct fm33256b {
+	struct mutex lock;
+	struct regmap *regmap_pc;
+	struct regmap *regmap_fram;
+};
+
+#endif /* __LINUX_MFD_FM33256B_H */
-- 
2.1.4

WARNING: multiple messages have this Message-ID (diff)
From: Jeppe Ledet-Pedersen <jlp@gomspace.com>
To: <lee.jones@linaro.org>, <arnd@arndb.de>,
	<gregkh@linuxfoundation.org>,
	<alexandre.belloni@free-electrons.com>, <a.zummo@towertech.it>,
	<robh+dt@kernel.org>, <pawel.moll@arm.com>,
	<mark.rutland@arm.com>, <ijc+devicetree@hellion.org.uk>,
	<galak@codeaurora.org>
Cc: <rtc-linux@googlegroups.com>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	Jeppe Ledet-Pedersen <jlp@gomspace.com>
Subject: [rtc-linux] [PATCH 1/3] mfd: add Cypress FM33256B Processor Companion driver
Date: Wed, 20 Apr 2016 13:07:49 +0200	[thread overview]
Message-ID: <1461150471-23163-2-git-send-email-jlp@gomspace.com> (raw)
In-Reply-To: <1461150471-23163-1-git-send-email-jlp@gomspace.com>

This patch adds support for the Cypress Semiconductor FM33256B processor
companion. The device contains a 256 kbit FRAM, an RTC, a supply voltage
monitor, and a watchdog timer.

Signed-off-by: Jeppe Ledet-Pedersen <jlp@gomspace.com>
---
 Documentation/devicetree/bindings/mfd/fm33256b.txt |  30 ++
 MAINTAINERS                                        |   6 +
 drivers/mfd/Kconfig                                |  16 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/fm33256b.c                             | 488 +++++++++++++++++++++
 include/linux/mfd/fm33256b.h                       |  76 ++++
 6 files changed, 617 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/fm33256b.txt
 create mode 100644 drivers/mfd/fm33256b.c
 create mode 100644 include/linux/mfd/fm33256b.h

diff --git a/Documentation/devicetree/bindings/mfd/fm33256b.txt b/Documentation/devicetree/bindings/mfd/fm33256b.txt
new file mode 100644
index 0000000..6591c94
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/fm33256b.txt
@@ -0,0 +1,30 @@
+Device-tree bindings for Cypress Semiconductor FM33256B Processor Companion
+---------------------------------------------------------------------------
+
+Required properties:
+- compatible: must be "cypress,fm33256b".
+- reg: SPI chip select
+- spi-max-frequency: Max SPI frequency to use (< 16000000)
+
+Optional properties:
+- cypress,charge-enabled: enable trickle charger
+- cypress,charge-fast: enable fast (1 mA) charging
+
+The MFD exposes two subdevices:
+- The FRAM: "cypress,fm33256b-fram"
+- The RTC: "cypress,fm33256b-rtc"
+
+Example:
+
+spi1: spi@f800800 {
+	status = "okay";
+	cs-gpios = <&pioC 25 0>;
+
+	fm33256b@0 {
+		compatible = "cypress,fm33256b";
+		spi-max-frequency = <10000000>;
+		cypress,charge-enabled;
+		cypress,charge-fast;
+		reg = <0>;
+	};
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 1d5b4be..87b5023 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3382,6 +3382,12 @@ T:	git git://linuxtv.org/anttip/media_tree.git
 S:	Maintained
 F:	drivers/media/common/cypress_firmware*
 
+CYPRESS FM33256B PROCESSOR COMPANION DRIVER
+M:	Jeppe Ledet-Pedersen <jlp@gomspace.com>
+S:	Maintained
+F:	include/linux/mfd/fm33256b.h
+F:	drivers/mfd/fm33256b.c
+
 CYTTSP TOUCHSCREEN DRIVER
 M:	Ferruh Yigit <fery@cypress.com>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index eea61e3..3a0d3a3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -93,6 +93,22 @@ config MFD_ATMEL_HLCDC
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_FM33256B
+	tristate "Cypress FM33256 Processor Companion support"
+	select MFD_CORE
+	select REGMAP_SPI
+	depends on OF
+	help
+	  If you say yes here you get support for the Cypress FM33256B
+	  Processor Companion device.
+
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+	  This driver can also be built as a module. If so the module
+	  will be called fm33256b.
+
 config MFD_BCM590XX
 	tristate "Broadcom BCM590xx PMUs"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5eaa6465d..a13728d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
+obj-$(CONFIG_MFD_FM33256B)	+= fm33256b.o
diff --git a/drivers/mfd/fm33256b.c b/drivers/mfd/fm33256b.c
new file mode 100644
index 0000000..880ccdd
--- /dev/null
+++ b/drivers/mfd/fm33256b.c
@@ -0,0 +1,488 @@
+/*
+ * Cypress FM33256B Processor Companion Driver
+ *
+ * Copyright (C) 2016 GomSpace ApS
+ *
+ * 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.
+ *
+ * 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/mfd/fm33256b.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/of.h>
+
+static const struct mfd_cell fm33256b_cells[] = {
+	{
+		.name = "fm33256b-rtc",
+		.of_compatible = "cypress,fm33256b-rtc",
+	},
+	{
+		.name = "fm33256b-fram",
+		.of_compatible = "cypress,fm33256b-fram",
+	},
+};
+
+static int fm33256b_io(struct spi_device *spi, bool write_enable,
+		       uint8_t *out, uint8_t *in, size_t len)
+{
+	struct spi_message m;
+	struct fm33256b *fm33256b = dev_get_drvdata(&spi->dev);
+
+	uint8_t write_out[1] = {FM33256B_OP_WREN};
+
+	/* Payload transfer */
+	struct spi_transfer t = {
+		.tx_buf = out,
+		.rx_buf = in,
+		.len = len,
+	};
+
+	mutex_lock(&fm33256b->lock);
+
+	/* CS must go high for the write enable latch to be enabled,
+	 * so we have to split this in two transfers.
+	 */
+	if (write_enable)
+		spi_write(spi, write_out, 1);
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	mutex_unlock(&fm33256b->lock);
+
+	return 0;
+}
+
+static int fm33256b_read_status(struct spi_device *spi, uint8_t *status)
+{
+	int ret;
+	uint8_t out[2] = {FM33256B_OP_RDSR, 0xff};
+	uint8_t in[2];
+
+	ret = fm33256b_io(spi, false, out, in, 2);
+	if (ret < 0)
+		return ret;
+
+	*status = in[1];
+
+	return 0;
+}
+
+static int fm33256b_write_status(struct spi_device *spi, uint8_t status)
+{
+	uint8_t out[2] = {FM33256B_OP_WRSR, status};
+	uint8_t in[2];
+
+	return fm33256b_io(spi, true, out, in, 2);
+}
+
+static int fm33256b_write_fram(struct spi_device *spi, uint16_t addr,
+			       const uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_WRITE;
+	out[1] = (addr >> 8) & 0xff;
+	out[2] = addr & 0xff;
+	memcpy(&out[3], data, len);
+
+	ret = fm33256b_io(spi, true, out, in, 3 + len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_read_fram(struct spi_device *spi, uint16_t addr,
+			      uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_READ;
+	out[1] = (addr >> 8) & 0xff;
+	out[2] = addr & 0xff;
+	memset(&out[3], 0xff, len);
+
+	ret = fm33256b_io(spi, false, out, in, 3 + len);
+	if (ret == 0)
+		memcpy(data, &in[3], len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_write_pc(struct spi_device *spi, uint8_t reg,
+			     const uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_WRPC;
+	out[1] = reg;
+	memcpy(&out[2], data, len);
+
+	ret = fm33256b_io(spi, true, out, in, 2 + len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_read_pc(struct spi_device *spi, uint8_t reg,
+			    uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_RDPC;
+	out[1] = reg;
+	memset(&out[2], 0xff, len);
+
+	ret = fm33256b_io(spi, false, out, in, 2 + len);
+	if (ret == 0)
+		memcpy(data, &in[2], len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_pc_regmap_read(void *context, const void *reg,
+				   size_t reg_size, void *val,
+				   size_t val_size)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	if (reg_size != 1)
+		return -ENOTSUPP;
+
+	return fm33256b_read_pc(spi, *(uint8_t *)reg, val, val_size);
+}
+
+static int fm33256b_pc_regmap_write(void *context, const void *data,
+				    size_t count)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *out = data;
+	const uint8_t *val = &out[1];
+
+	uint8_t reg = out[0];
+
+	return fm33256b_write_pc(spi, reg, val, count - sizeof(reg));
+}
+
+static int fm33256b_fram_regmap_read(void *context, const void *reg,
+				     size_t reg_size, void *val,
+				     size_t val_size)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *addrp = reg;
+
+	uint16_t addr = ((uint16_t)addrp[0] << 8) | addrp[1];
+
+	if (reg_size != 2)
+		return -ENOTSUPP;
+
+	return fm33256b_read_fram(spi, addr, val, val_size);
+}
+
+static int fm33256b_fram_regmap_write(void *context, const void *data,
+				      size_t count)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *out = data;
+	const uint8_t *val = &out[2];
+
+	uint16_t addr = ((uint16_t)out[0] << 8) | out[1];
+
+	return fm33256b_write_fram(spi, addr, val, count - sizeof(addr));
+}
+
+static ssize_t fm33256b_bp_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+	uint8_t status, bp;
+
+	ret = fm33256b_read_status(spi, &status);
+	if (ret < 0)
+		return ret;
+
+	bp = (status & 0x0c) >> 2;
+
+	return snprintf(buf, PAGE_SIZE, "%hhu\n", bp);
+}
+
+static ssize_t fm33256b_bp_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+	uint8_t status, bp;
+	unsigned long input;
+
+	ret = kstrtoul(buf, 10, &input);
+	if (ret < 0)
+		return ret;
+
+	if (input > 3)
+		return -EINVAL;
+
+	bp = (uint8_t)input << 2;
+
+	ret = fm33256b_read_status(spi, &status);
+	if (ret < 0)
+		return ret;
+
+	status = (status & 0xf3) | bp;
+
+	ret = fm33256b_write_status(spi, status);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t fm33256b_serial_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	char serial[9];
+	int ret;
+
+	ret = fm33256b_read_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8);
+	if (ret < 0)
+		return ret;
+
+	serial[8] = '\0';
+
+	return snprintf(buf, PAGE_SIZE, "%-8s\n", serial);
+}
+
+static ssize_t fm33256b_serial_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	char serial[9];
+	int ret;
+
+	if (sscanf(buf, "%8s", serial) != 1)
+		return -EINVAL;
+
+	ret = fm33256b_write_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+DEVICE_ATTR(bp, S_IWUSR | S_IRUGO,
+	    fm33256b_bp_show, fm33256b_bp_store);
+DEVICE_ATTR(serial, S_IWUSR | S_IRUGO,
+	    fm33256b_serial_show, fm33256b_serial_store);
+
+/* Processor Companion Register Map */
+static const struct regmap_config fm33256b_pc_regmap_conf = {
+	.name = "pc",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = FM33256B_MAX_REGISTER,
+};
+
+static struct regmap_bus fm33256b_pc_regmap_bus = {
+	.write = fm33256b_pc_regmap_write,
+	.read = fm33256b_pc_regmap_read,
+};
+
+/* FRAM Register Map */
+static const struct regmap_config fm33256b_fram_regmap_conf = {
+	.name = "fram",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = FM33256B_MAX_FRAM,
+};
+
+static struct regmap_bus fm33256b_fram_regmap_bus = {
+	.write = fm33256b_fram_regmap_write,
+	.read = fm33256b_fram_regmap_read,
+};
+
+static int fm33256b_setup(struct spi_device *spi, struct fm33256b *fm33256b)
+{
+	int ret;
+	uint8_t companion_ctl = FM33256B_ALSW, rtc_alarm_ctl = 0;
+
+	/* Setup charger control from DT */
+	if (of_get_property(spi->dev.of_node, "cypress,charge-enabled", NULL))
+		companion_ctl |= FM33256B_VBC;
+
+	if (of_get_property(spi->dev.of_node, "cypress,charge-fast", NULL))
+		companion_ctl |= FM33256B_FC;
+
+	/* Setup charging if enabled */
+	ret = regmap_write(fm33256b->regmap_pc,
+			   FM33256B_COMPANION_CONTROL_REG,
+			   companion_ctl);
+	if (ret < 0)
+		return ret;
+
+	/* Enable 32 kHz oscillator */
+	ret = regmap_write(fm33256b->regmap_pc,
+			   FM33256B_RTC_ALARM_CONTROL_REG,
+			   rtc_alarm_ctl);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int fm33256b_probe(struct spi_device *spi)
+{
+	int ret;
+	struct device *dev = &spi->dev;
+	struct fm33256b *fm33256b;
+
+	fm33256b = devm_kzalloc(dev, sizeof(*fm33256b), GFP_KERNEL);
+	if (!fm33256b)
+		return -ENOMEM;
+
+	mutex_init(&fm33256b->lock);
+
+	spi->mode = SPI_MODE_0;
+	spi->max_speed_hz = spi->max_speed_hz ? : 8000000;
+
+	ret = spi_setup(spi);
+	if (ret < 0)
+		return ret;
+
+	/* Setup processor companion regmap */
+	fm33256b->regmap_pc =
+		devm_regmap_init(dev, &fm33256b_pc_regmap_bus,
+				 dev, &fm33256b_pc_regmap_conf);
+	if (IS_ERR(fm33256b->regmap_pc))
+		return PTR_ERR(fm33256b->regmap_pc);
+
+	/* Setup FRAM regmap */
+	fm33256b->regmap_fram =
+		devm_regmap_init(dev, &fm33256b_fram_regmap_bus,
+				 dev, &fm33256b_fram_regmap_conf);
+	if (IS_ERR(fm33256b->regmap_fram))
+		return PTR_ERR(fm33256b->regmap_fram);
+
+	dev_set_drvdata(dev, fm33256b);
+
+	ret = fm33256b_setup(spi, fm33256b);
+	if (ret < 0)
+		return ret;
+
+	/* Create sysfs entries */
+	ret = device_create_file(&spi->dev, &dev_attr_bp);
+	if (ret < 0)
+		return ret;
+
+	ret = device_create_file(&spi->dev, &dev_attr_serial);
+	if (ret < 0) {
+		device_remove_file(&spi->dev, &dev_attr_bp);
+		return ret;
+	}
+
+	return mfd_add_devices(dev, -1, fm33256b_cells,
+			       ARRAY_SIZE(fm33256b_cells),
+			       NULL, 0, NULL);
+}
+
+static int fm33256b_remove(struct spi_device *spi)
+{
+	mfd_remove_devices(&spi->dev);
+	device_remove_file(&spi->dev, &dev_attr_serial);
+	device_remove_file(&spi->dev, &dev_attr_bp);
+
+	return 0;
+}
+
+static const struct of_device_id fm33256b_dt_ids[] = {
+	{ .compatible = "cypress,fm33256b" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, fm33256b_dt_ids);
+
+static struct spi_driver fm33256b_spi_driver = {
+	.driver = {
+		.name = "fm33256b",
+		.owner = THIS_MODULE,
+		.of_match_table = fm33256b_dt_ids,
+	},
+	.probe = fm33256b_probe,
+	.remove = fm33256b_remove,
+};
+module_spi_driver(fm33256b_spi_driver);
+
+MODULE_ALIAS("spi:fm33256b");
+MODULE_DESCRIPTION("Cypress FM33256B Processor Companion Driver");
+MODULE_AUTHOR("Jeppe Ledet-Pedersen <jlp@gomspace.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/fm33256b.h b/include/linux/mfd/fm33256b.h
new file mode 100644
index 0000000..cd1e37a
--- /dev/null
+++ b/include/linux/mfd/fm33256b.h
@@ -0,0 +1,76 @@
+/*
+ * Cypress FM33256B Processor Companion Driver
+ *
+ * Copyright (C) 2016 GomSpace ApS
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_MFD_FM33256B_H
+#define __LINUX_MFD_FM33256B_H
+
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+/* Opcodes */
+#define FM33256B_OP_WREN		0x06
+#define FM33256B_OP_WRDI		0x04
+#define FM33256B_OP_RDSR		0x05
+#define FM33256B_OP_WRSR		0x01
+#define FM33256B_OP_READ		0x03
+#define FM33256B_OP_WRITE		0x02
+#define FM33256B_OP_RDPC		0x13
+#define FM33256B_OP_WRPC		0x12
+
+/* RTC/Processor Companion Register Map */
+#define FM33256B_ALARM_MONTH		0x1D
+#define FM33256B_COMPANION_CONTROL_REG	0x18
+#define FM33256B_SERIAL_BYTE0_REG	0x10
+#define FM33256B_YEARS_REG		0x08
+#define FM33256B_MONTH_REG		0x07
+#define FM33256B_DATE_REG		0x06
+#define FM33256B_DAY_REG		0x05
+#define FM33256B_HOURS_REG		0x04
+#define FM33256B_MINUTES_REG		0x03
+#define FM33256B_SECONDS_REG		0x02
+#define FM33256B_CAL_CONTROL_REG	0x01
+#define FM33256B_RTC_ALARM_CONTROL_REG	0x00
+
+/* Companion Control bits */
+#define FM33256B_ALSW			BIT(6)
+#define FM33256B_VBC			BIT(3)
+#define FM33256B_FC			BIT(2)
+
+/* RTC/Alarm Control bits */
+#define FM33256B_R			BIT(0)
+#define FM33256B_W			BIT(1)
+#define FM33256B_CAL			BIT(2)
+#define FM33256B_OSCEN			BIT(7)
+
+/* Limits */
+#define FM33256B_MAX_REGISTER		FM33256B_ALARM_MONTH
+#define FM33256B_MAX_FRAM		(32 * 1024) /* 256 kb */
+
+/**
+ * Structure shared by the MFD device and its subdevices.
+ *
+ * @regmap: register map used to access registers
+ */
+struct fm33256b {
+	struct mutex lock;
+	struct regmap *regmap_pc;
+	struct regmap *regmap_fram;
+};
+
+#endif /* __LINUX_MFD_FM33256B_H */
-- 
2.1.4

-- 
-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

WARNING: multiple messages have this Message-ID (diff)
From: Jeppe Ledet-Pedersen <jlp-jTq9jD7AHYRWk0Htik3J/w@public.gmane.org>
To: lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org,
	arnd-r2nGTMty4D4@public.gmane.org,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ@public.gmane.org,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	pawel.moll-5wv7dgnIgG8@public.gmane.org,
	mark.rutland-5wv7dgnIgG8@public.gmane.org,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org,
	galak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org
Cc: rtc-linux-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Jeppe Ledet-Pedersen
	<jlp-jTq9jD7AHYRWk0Htik3J/w@public.gmane.org>
Subject: [PATCH 1/3] mfd: add Cypress FM33256B Processor Companion driver
Date: Wed, 20 Apr 2016 13:07:49 +0200	[thread overview]
Message-ID: <1461150471-23163-2-git-send-email-jlp@gomspace.com> (raw)
In-Reply-To: <1461150471-23163-1-git-send-email-jlp-jTq9jD7AHYRWk0Htik3J/w@public.gmane.org>

This patch adds support for the Cypress Semiconductor FM33256B processor
companion. The device contains a 256 kbit FRAM, an RTC, a supply voltage
monitor, and a watchdog timer.

Signed-off-by: Jeppe Ledet-Pedersen <jlp-jTq9jD7AHYRWk0Htik3J/w@public.gmane.org>
---
 Documentation/devicetree/bindings/mfd/fm33256b.txt |  30 ++
 MAINTAINERS                                        |   6 +
 drivers/mfd/Kconfig                                |  16 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/fm33256b.c                             | 488 +++++++++++++++++++++
 include/linux/mfd/fm33256b.h                       |  76 ++++
 6 files changed, 617 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/fm33256b.txt
 create mode 100644 drivers/mfd/fm33256b.c
 create mode 100644 include/linux/mfd/fm33256b.h

diff --git a/Documentation/devicetree/bindings/mfd/fm33256b.txt b/Documentation/devicetree/bindings/mfd/fm33256b.txt
new file mode 100644
index 0000000..6591c94
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/fm33256b.txt
@@ -0,0 +1,30 @@
+Device-tree bindings for Cypress Semiconductor FM33256B Processor Companion
+---------------------------------------------------------------------------
+
+Required properties:
+- compatible: must be "cypress,fm33256b".
+- reg: SPI chip select
+- spi-max-frequency: Max SPI frequency to use (< 16000000)
+
+Optional properties:
+- cypress,charge-enabled: enable trickle charger
+- cypress,charge-fast: enable fast (1 mA) charging
+
+The MFD exposes two subdevices:
+- The FRAM: "cypress,fm33256b-fram"
+- The RTC: "cypress,fm33256b-rtc"
+
+Example:
+
+spi1: spi@f800800 {
+	status = "okay";
+	cs-gpios = <&pioC 25 0>;
+
+	fm33256b@0 {
+		compatible = "cypress,fm33256b";
+		spi-max-frequency = <10000000>;
+		cypress,charge-enabled;
+		cypress,charge-fast;
+		reg = <0>;
+	};
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 1d5b4be..87b5023 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3382,6 +3382,12 @@ T:	git git://linuxtv.org/anttip/media_tree.git
 S:	Maintained
 F:	drivers/media/common/cypress_firmware*
 
+CYPRESS FM33256B PROCESSOR COMPANION DRIVER
+M:	Jeppe Ledet-Pedersen <jlp-jTq9jD7AHYRWk0Htik3J/w@public.gmane.org>
+S:	Maintained
+F:	include/linux/mfd/fm33256b.h
+F:	drivers/mfd/fm33256b.c
+
 CYTTSP TOUCHSCREEN DRIVER
 M:	Ferruh Yigit <fery-+wT8y+m8/X5BDgjK7y7TUQ@public.gmane.org>
 L:	linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index eea61e3..3a0d3a3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -93,6 +93,22 @@ config MFD_ATMEL_HLCDC
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_FM33256B
+	tristate "Cypress FM33256 Processor Companion support"
+	select MFD_CORE
+	select REGMAP_SPI
+	depends on OF
+	help
+	  If you say yes here you get support for the Cypress FM33256B
+	  Processor Companion device.
+
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+	  This driver can also be built as a module. If so the module
+	  will be called fm33256b.
+
 config MFD_BCM590XX
 	tristate "Broadcom BCM590xx PMUs"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5eaa6465d..a13728d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -203,3 +203,4 @@ intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
+obj-$(CONFIG_MFD_FM33256B)	+= fm33256b.o
diff --git a/drivers/mfd/fm33256b.c b/drivers/mfd/fm33256b.c
new file mode 100644
index 0000000..880ccdd
--- /dev/null
+++ b/drivers/mfd/fm33256b.c
@@ -0,0 +1,488 @@
+/*
+ * Cypress FM33256B Processor Companion Driver
+ *
+ * Copyright (C) 2016 GomSpace ApS
+ *
+ * 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.
+ *
+ * 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/mfd/fm33256b.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/of.h>
+
+static const struct mfd_cell fm33256b_cells[] = {
+	{
+		.name = "fm33256b-rtc",
+		.of_compatible = "cypress,fm33256b-rtc",
+	},
+	{
+		.name = "fm33256b-fram",
+		.of_compatible = "cypress,fm33256b-fram",
+	},
+};
+
+static int fm33256b_io(struct spi_device *spi, bool write_enable,
+		       uint8_t *out, uint8_t *in, size_t len)
+{
+	struct spi_message m;
+	struct fm33256b *fm33256b = dev_get_drvdata(&spi->dev);
+
+	uint8_t write_out[1] = {FM33256B_OP_WREN};
+
+	/* Payload transfer */
+	struct spi_transfer t = {
+		.tx_buf = out,
+		.rx_buf = in,
+		.len = len,
+	};
+
+	mutex_lock(&fm33256b->lock);
+
+	/* CS must go high for the write enable latch to be enabled,
+	 * so we have to split this in two transfers.
+	 */
+	if (write_enable)
+		spi_write(spi, write_out, 1);
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	mutex_unlock(&fm33256b->lock);
+
+	return 0;
+}
+
+static int fm33256b_read_status(struct spi_device *spi, uint8_t *status)
+{
+	int ret;
+	uint8_t out[2] = {FM33256B_OP_RDSR, 0xff};
+	uint8_t in[2];
+
+	ret = fm33256b_io(spi, false, out, in, 2);
+	if (ret < 0)
+		return ret;
+
+	*status = in[1];
+
+	return 0;
+}
+
+static int fm33256b_write_status(struct spi_device *spi, uint8_t status)
+{
+	uint8_t out[2] = {FM33256B_OP_WRSR, status};
+	uint8_t in[2];
+
+	return fm33256b_io(spi, true, out, in, 2);
+}
+
+static int fm33256b_write_fram(struct spi_device *spi, uint16_t addr,
+			       const uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_WRITE;
+	out[1] = (addr >> 8) & 0xff;
+	out[2] = addr & 0xff;
+	memcpy(&out[3], data, len);
+
+	ret = fm33256b_io(spi, true, out, in, 3 + len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_read_fram(struct spi_device *spi, uint16_t addr,
+			      uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_READ;
+	out[1] = (addr >> 8) & 0xff;
+	out[2] = addr & 0xff;
+	memset(&out[3], 0xff, len);
+
+	ret = fm33256b_io(spi, false, out, in, 3 + len);
+	if (ret == 0)
+		memcpy(data, &in[3], len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_write_pc(struct spi_device *spi, uint8_t reg,
+			     const uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_WRPC;
+	out[1] = reg;
+	memcpy(&out[2], data, len);
+
+	ret = fm33256b_io(spi, true, out, in, 2 + len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_read_pc(struct spi_device *spi, uint8_t reg,
+			    uint8_t *data, size_t len)
+{
+	int ret;
+	uint8_t *out = NULL, *in = NULL;
+
+	out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL);
+	if (!out || !in) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	out[0] = FM33256B_OP_RDPC;
+	out[1] = reg;
+	memset(&out[2], 0xff, len);
+
+	ret = fm33256b_io(spi, false, out, in, 2 + len);
+	if (ret == 0)
+		memcpy(data, &in[2], len);
+
+out:
+	devm_kfree(&spi->dev, out);
+	devm_kfree(&spi->dev, in);
+
+	return ret;
+}
+
+static int fm33256b_pc_regmap_read(void *context, const void *reg,
+				   size_t reg_size, void *val,
+				   size_t val_size)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	if (reg_size != 1)
+		return -ENOTSUPP;
+
+	return fm33256b_read_pc(spi, *(uint8_t *)reg, val, val_size);
+}
+
+static int fm33256b_pc_regmap_write(void *context, const void *data,
+				    size_t count)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *out = data;
+	const uint8_t *val = &out[1];
+
+	uint8_t reg = out[0];
+
+	return fm33256b_write_pc(spi, reg, val, count - sizeof(reg));
+}
+
+static int fm33256b_fram_regmap_read(void *context, const void *reg,
+				     size_t reg_size, void *val,
+				     size_t val_size)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *addrp = reg;
+
+	uint16_t addr = ((uint16_t)addrp[0] << 8) | addrp[1];
+
+	if (reg_size != 2)
+		return -ENOTSUPP;
+
+	return fm33256b_read_fram(spi, addr, val, val_size);
+}
+
+static int fm33256b_fram_regmap_write(void *context, const void *data,
+				      size_t count)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	const uint8_t *out = data;
+	const uint8_t *val = &out[2];
+
+	uint16_t addr = ((uint16_t)out[0] << 8) | out[1];
+
+	return fm33256b_write_fram(spi, addr, val, count - sizeof(addr));
+}
+
+static ssize_t fm33256b_bp_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+	uint8_t status, bp;
+
+	ret = fm33256b_read_status(spi, &status);
+	if (ret < 0)
+		return ret;
+
+	bp = (status & 0x0c) >> 2;
+
+	return snprintf(buf, PAGE_SIZE, "%hhu\n", bp);
+}
+
+static ssize_t fm33256b_bp_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+	uint8_t status, bp;
+	unsigned long input;
+
+	ret = kstrtoul(buf, 10, &input);
+	if (ret < 0)
+		return ret;
+
+	if (input > 3)
+		return -EINVAL;
+
+	bp = (uint8_t)input << 2;
+
+	ret = fm33256b_read_status(spi, &status);
+	if (ret < 0)
+		return ret;
+
+	status = (status & 0xf3) | bp;
+
+	ret = fm33256b_write_status(spi, status);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t fm33256b_serial_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	char serial[9];
+	int ret;
+
+	ret = fm33256b_read_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8);
+	if (ret < 0)
+		return ret;
+
+	serial[8] = '\0';
+
+	return snprintf(buf, PAGE_SIZE, "%-8s\n", serial);
+}
+
+static ssize_t fm33256b_serial_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	char serial[9];
+	int ret;
+
+	if (sscanf(buf, "%8s", serial) != 1)
+		return -EINVAL;
+
+	ret = fm33256b_write_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+DEVICE_ATTR(bp, S_IWUSR | S_IRUGO,
+	    fm33256b_bp_show, fm33256b_bp_store);
+DEVICE_ATTR(serial, S_IWUSR | S_IRUGO,
+	    fm33256b_serial_show, fm33256b_serial_store);
+
+/* Processor Companion Register Map */
+static const struct regmap_config fm33256b_pc_regmap_conf = {
+	.name = "pc",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = FM33256B_MAX_REGISTER,
+};
+
+static struct regmap_bus fm33256b_pc_regmap_bus = {
+	.write = fm33256b_pc_regmap_write,
+	.read = fm33256b_pc_regmap_read,
+};
+
+/* FRAM Register Map */
+static const struct regmap_config fm33256b_fram_regmap_conf = {
+	.name = "fram",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = FM33256B_MAX_FRAM,
+};
+
+static struct regmap_bus fm33256b_fram_regmap_bus = {
+	.write = fm33256b_fram_regmap_write,
+	.read = fm33256b_fram_regmap_read,
+};
+
+static int fm33256b_setup(struct spi_device *spi, struct fm33256b *fm33256b)
+{
+	int ret;
+	uint8_t companion_ctl = FM33256B_ALSW, rtc_alarm_ctl = 0;
+
+	/* Setup charger control from DT */
+	if (of_get_property(spi->dev.of_node, "cypress,charge-enabled", NULL))
+		companion_ctl |= FM33256B_VBC;
+
+	if (of_get_property(spi->dev.of_node, "cypress,charge-fast", NULL))
+		companion_ctl |= FM33256B_FC;
+
+	/* Setup charging if enabled */
+	ret = regmap_write(fm33256b->regmap_pc,
+			   FM33256B_COMPANION_CONTROL_REG,
+			   companion_ctl);
+	if (ret < 0)
+		return ret;
+
+	/* Enable 32 kHz oscillator */
+	ret = regmap_write(fm33256b->regmap_pc,
+			   FM33256B_RTC_ALARM_CONTROL_REG,
+			   rtc_alarm_ctl);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int fm33256b_probe(struct spi_device *spi)
+{
+	int ret;
+	struct device *dev = &spi->dev;
+	struct fm33256b *fm33256b;
+
+	fm33256b = devm_kzalloc(dev, sizeof(*fm33256b), GFP_KERNEL);
+	if (!fm33256b)
+		return -ENOMEM;
+
+	mutex_init(&fm33256b->lock);
+
+	spi->mode = SPI_MODE_0;
+	spi->max_speed_hz = spi->max_speed_hz ? : 8000000;
+
+	ret = spi_setup(spi);
+	if (ret < 0)
+		return ret;
+
+	/* Setup processor companion regmap */
+	fm33256b->regmap_pc =
+		devm_regmap_init(dev, &fm33256b_pc_regmap_bus,
+				 dev, &fm33256b_pc_regmap_conf);
+	if (IS_ERR(fm33256b->regmap_pc))
+		return PTR_ERR(fm33256b->regmap_pc);
+
+	/* Setup FRAM regmap */
+	fm33256b->regmap_fram =
+		devm_regmap_init(dev, &fm33256b_fram_regmap_bus,
+				 dev, &fm33256b_fram_regmap_conf);
+	if (IS_ERR(fm33256b->regmap_fram))
+		return PTR_ERR(fm33256b->regmap_fram);
+
+	dev_set_drvdata(dev, fm33256b);
+
+	ret = fm33256b_setup(spi, fm33256b);
+	if (ret < 0)
+		return ret;
+
+	/* Create sysfs entries */
+	ret = device_create_file(&spi->dev, &dev_attr_bp);
+	if (ret < 0)
+		return ret;
+
+	ret = device_create_file(&spi->dev, &dev_attr_serial);
+	if (ret < 0) {
+		device_remove_file(&spi->dev, &dev_attr_bp);
+		return ret;
+	}
+
+	return mfd_add_devices(dev, -1, fm33256b_cells,
+			       ARRAY_SIZE(fm33256b_cells),
+			       NULL, 0, NULL);
+}
+
+static int fm33256b_remove(struct spi_device *spi)
+{
+	mfd_remove_devices(&spi->dev);
+	device_remove_file(&spi->dev, &dev_attr_serial);
+	device_remove_file(&spi->dev, &dev_attr_bp);
+
+	return 0;
+}
+
+static const struct of_device_id fm33256b_dt_ids[] = {
+	{ .compatible = "cypress,fm33256b" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, fm33256b_dt_ids);
+
+static struct spi_driver fm33256b_spi_driver = {
+	.driver = {
+		.name = "fm33256b",
+		.owner = THIS_MODULE,
+		.of_match_table = fm33256b_dt_ids,
+	},
+	.probe = fm33256b_probe,
+	.remove = fm33256b_remove,
+};
+module_spi_driver(fm33256b_spi_driver);
+
+MODULE_ALIAS("spi:fm33256b");
+MODULE_DESCRIPTION("Cypress FM33256B Processor Companion Driver");
+MODULE_AUTHOR("Jeppe Ledet-Pedersen <jlp-jTq9jD7AHYRWk0Htik3J/w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/fm33256b.h b/include/linux/mfd/fm33256b.h
new file mode 100644
index 0000000..cd1e37a
--- /dev/null
+++ b/include/linux/mfd/fm33256b.h
@@ -0,0 +1,76 @@
+/*
+ * Cypress FM33256B Processor Companion Driver
+ *
+ * Copyright (C) 2016 GomSpace ApS
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_MFD_FM33256B_H
+#define __LINUX_MFD_FM33256B_H
+
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+/* Opcodes */
+#define FM33256B_OP_WREN		0x06
+#define FM33256B_OP_WRDI		0x04
+#define FM33256B_OP_RDSR		0x05
+#define FM33256B_OP_WRSR		0x01
+#define FM33256B_OP_READ		0x03
+#define FM33256B_OP_WRITE		0x02
+#define FM33256B_OP_RDPC		0x13
+#define FM33256B_OP_WRPC		0x12
+
+/* RTC/Processor Companion Register Map */
+#define FM33256B_ALARM_MONTH		0x1D
+#define FM33256B_COMPANION_CONTROL_REG	0x18
+#define FM33256B_SERIAL_BYTE0_REG	0x10
+#define FM33256B_YEARS_REG		0x08
+#define FM33256B_MONTH_REG		0x07
+#define FM33256B_DATE_REG		0x06
+#define FM33256B_DAY_REG		0x05
+#define FM33256B_HOURS_REG		0x04
+#define FM33256B_MINUTES_REG		0x03
+#define FM33256B_SECONDS_REG		0x02
+#define FM33256B_CAL_CONTROL_REG	0x01
+#define FM33256B_RTC_ALARM_CONTROL_REG	0x00
+
+/* Companion Control bits */
+#define FM33256B_ALSW			BIT(6)
+#define FM33256B_VBC			BIT(3)
+#define FM33256B_FC			BIT(2)
+
+/* RTC/Alarm Control bits */
+#define FM33256B_R			BIT(0)
+#define FM33256B_W			BIT(1)
+#define FM33256B_CAL			BIT(2)
+#define FM33256B_OSCEN			BIT(7)
+
+/* Limits */
+#define FM33256B_MAX_REGISTER		FM33256B_ALARM_MONTH
+#define FM33256B_MAX_FRAM		(32 * 1024) /* 256 kb */
+
+/**
+ * Structure shared by the MFD device and its subdevices.
+ *
+ * @regmap: register map used to access registers
+ */
+struct fm33256b {
+	struct mutex lock;
+	struct regmap *regmap_pc;
+	struct regmap *regmap_fram;
+};
+
+#endif /* __LINUX_MFD_FM33256B_H */
-- 
2.1.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

  reply	other threads:[~2016-04-20 11:26 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-20 11:07 [PATCH 0/3] Cypress FM33256B processor companion support Jeppe Ledet-Pedersen
2016-04-20 11:07 ` Jeppe Ledet-Pedersen
2016-04-20 11:07 ` [rtc-linux] " Jeppe Ledet-Pedersen
2016-04-20 11:07 ` Jeppe Ledet-Pedersen [this message]
2016-04-20 11:07   ` [PATCH 1/3] mfd: add Cypress FM33256B Processor Companion driver Jeppe Ledet-Pedersen
2016-04-20 11:07   ` [rtc-linux] " Jeppe Ledet-Pedersen
2016-04-22  0:47   ` kbuild test robot
2016-04-22  0:47     ` kbuild test robot
2016-04-22  0:47     ` [rtc-linux] " kbuild test robot
2016-04-22 19:32   ` Rob Herring
2016-04-22 19:32     ` Rob Herring
2016-04-22 19:32     ` [rtc-linux] " Rob Herring
2016-04-22 20:11     ` Alexandre Belloni
2016-04-22 20:11       ` [rtc-linux] " Alexandre Belloni
2016-04-26 14:31       ` Jeppe Ledet-Pedersen
2016-04-26 14:31         ` Jeppe Ledet-Pedersen
2016-04-26 14:31         ` [rtc-linux] " Jeppe Ledet-Pedersen
2016-05-03  8:14         ` Alexandre Belloni
2016-05-03  8:14           ` Alexandre Belloni
2016-05-03  8:14           ` [rtc-linux] " Alexandre Belloni
2016-04-26 14:42     ` Jeppe Ledet-Pedersen
2016-04-26 14:42       ` Jeppe Ledet-Pedersen
2016-04-26 14:42       ` [rtc-linux] " Jeppe Ledet-Pedersen
2016-04-20 11:07 ` [PATCH 2/3] misc: eeprom: add Cypress FM33256B FRAM driver Jeppe Ledet-Pedersen
2016-04-20 11:07   ` Jeppe Ledet-Pedersen
2016-04-20 11:07   ` [rtc-linux] " Jeppe Ledet-Pedersen
2016-04-21 23:54   ` Alexandre Belloni
2016-04-21 23:54     ` [rtc-linux] " Alexandre Belloni
2016-04-26 12:08     ` Jeppe Ledet-Pedersen
2016-04-26 12:08       ` Jeppe Ledet-Pedersen
2016-04-26 12:08       ` [rtc-linux] " Jeppe Ledet-Pedersen
2016-04-20 11:07 ` [PATCH 3/3] rtc: add Cypress FM33256B RTC driver Jeppe Ledet-Pedersen
2016-04-20 11:07   ` Jeppe Ledet-Pedersen
2016-04-20 11:07   ` [rtc-linux] " Jeppe Ledet-Pedersen
2016-04-21 23:44   ` Alexandre Belloni
2016-04-21 23:44     ` [rtc-linux] " Alexandre Belloni
2016-04-26 12:17     ` Jeppe Ledet-Pedersen
2016-04-26 12:17       ` Jeppe Ledet-Pedersen
2016-04-26 12:17       ` [rtc-linux] " Jeppe Ledet-Pedersen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1461150471-23163-2-git-send-email-jlp@gomspace.com \
    --to=jlp@gomspace.com \
    --cc=a.zummo@towertech.it \
    --cc=alexandre.belloni@free-electrons.com \
    --cc=arnd@arndb.de \
    --cc=devicetree@vger.kernel.org \
    --cc=galak@codeaurora.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=ijc+devicetree@hellion.org.uk \
    --cc=lee.jones@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=pawel.moll@arm.com \
    --cc=robh+dt@kernel.org \
    --cc=rtc-linux@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.