Hi,
This series 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. Only the FRAM and RTC are supported by these
patches.
The device is implemented as an MFD device. The FRAM interface is currently
implemented as an SPI regmap, wrapped by a binary sysfs file. If another
interface would be a better choice, please let me know.
Thanks,
-Jeppe
Signed-off-by: Jeppe Ledet-Pedersen <[email protected]>
---
MAINTAINERS | 1 +
drivers/misc/eeprom/Kconfig | 12 ++++
drivers/misc/eeprom/Makefile | 1 +
drivers/misc/eeprom/fm33256b-fram.c | 110 ++++++++++++++++++++++++++++++++++++
4 files changed, 124 insertions(+)
create mode 100644 drivers/misc/eeprom/fm33256b-fram.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 87b5023..a8b71d8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3387,6 +3387,7 @@ M: Jeppe Ledet-Pedersen <[email protected]>
S: Maintained
F: include/linux/mfd/fm33256b.h
F: drivers/mfd/fm33256b.c
+F: drivers/misc/eeprom/fm33256b-fram.c
CYTTSP TOUCHSCREEN DRIVER
M: Ferruh Yigit <[email protected]>
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index cfc493c..2c0c26a3 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -102,4 +102,16 @@ config EEPROM_DIGSY_MTC_CFG
If unsure, say N.
+config EEPROM_FM33256B
+ tristate "Cypress FM33256B FRAM support"
+ depends on MFD_FM33256B
+ help
+ This is a driver for the FRAM found in the Cypress FM33256B Processor
+ Companion.
+
+ The FRAM is exported as a binary sysfs file.
+
+ This driver can also be built as a module. If so, the module
+ will be called fm33256b-fram.
+
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index fc1e81d..f991f0f 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
+obj-$(CONFIG_EEPROM_FM33256B) += fm33256b-fram.o
diff --git a/drivers/misc/eeprom/fm33256b-fram.c b/drivers/misc/eeprom/fm33256b-fram.c
new file mode 100644
index 0000000..fd3c2c9
--- /dev/null
+++ b/drivers/misc/eeprom/fm33256b-fram.c
@@ -0,0 +1,110 @@
+/*
+ * Cypress FM33256B Processor Companion FRAM 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+static ssize_t fm33256b_fram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct fm33256b *fm33256b = dev_get_drvdata(dev->parent);
+
+ ret = regmap_bulk_read(fm33256b->regmap_fram, off, buf, count);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t fm33256b_fram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct fm33256b *fm33256b = dev_get_drvdata(dev->parent);
+
+ ret = regmap_bulk_write(fm33256b->regmap_fram, off, buf, count);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static struct bin_attribute fm33256b_fram_attr = {
+ .attr = {
+ .name = "fram",
+ .mode = S_IWUSR | S_IRUGO,
+ },
+ .size = FM33256B_MAX_FRAM,
+ .read = fm33256b_fram_read,
+ .write = fm33256b_fram_write,
+};
+
+static int fm33256b_fram_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct fm33256b *fm33256b;
+
+ fm33256b = dev_get_drvdata(dev->parent);
+
+ ret = sysfs_create_bin_file(&(dev->kobj), &fm33256b_fram_attr);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int fm33256b_fram_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fm33256b *fm33256b;
+
+ fm33256b = dev_get_drvdata(dev->parent);
+
+ sysfs_remove_bin_file(&(dev->kobj), &fm33256b_fram_attr);
+
+ return 0;
+}
+
+static const struct of_device_id fm33256b_fram_dt_ids[] = {
+ { .compatible = "cypress,fm33256b-fram" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, fm33256b_fram_dt_ids);
+
+static struct platform_driver fm33256b_fram_driver = {
+ .driver = {
+ .name = "fm33256b-fram",
+ .of_match_table = fm33256b_fram_dt_ids,
+ },
+ .probe = fm33256b_fram_probe,
+ .remove = fm33256b_fram_remove,
+};
+module_platform_driver(fm33256b_fram_driver);
+
+MODULE_ALIAS("platform:fm33256b-fram");
+MODULE_AUTHOR("Jeppe Ledet-Pedersen <[email protected]>");
+MODULE_DESCRIPTION("Cypress FM33256B Processor Companion FRAM Driver");
+MODULE_LICENSE("GPL v2");
--
2.1.4
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 <[email protected]>
---
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 <[email protected]>
+S: Maintained
+F: include/linux/mfd/fm33256b.h
+F: drivers/mfd/fm33256b.c
+
CYTTSP TOUCHSCREEN DRIVER
M: Ferruh Yigit <[email protected]>
L: [email protected]
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 <[email protected]>");
+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
Signed-off-by: Jeppe Ledet-Pedersen <[email protected]>
---
MAINTAINERS | 1 +
drivers/rtc/Kconfig | 10 +++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-fm33256b.c | 166 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 178 insertions(+)
create mode 100644 drivers/rtc/rtc-fm33256b.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a8b71d8..9ccc285 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3388,6 +3388,7 @@ S: Maintained
F: include/linux/mfd/fm33256b.h
F: drivers/mfd/fm33256b.c
F: drivers/misc/eeprom/fm33256b-fram.c
+F: drivers/rtc/rtc-fm33256b.c
CYTTSP TOUCHSCREEN DRIVER
M: Ferruh Yigit <[email protected]>
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 3e84315..7856f0b 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -740,6 +740,16 @@ config RTC_DRV_MCP795
This driver can also be built as a module. If so the module
will be called rtc-mcp795.
+config RTC_DRV_FM33256B
+ tristate "Cypress FM33256B RTC support"
+ depends on MFD_FM33256B
+ help
+ If you say Y here you will get support for the RTC found in the
+ Cypress FM33256B Processor Companion.
+
+ This driver can also be built as a module. If so the module
+ will be called rtc-fm33256b.
+
endif # SPI_MASTER
#
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index ea28337..1478fb6 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
+obj-$(CONFIG_RTC_DRV_FM33256B) += rtc-fm33256b.o
obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
diff --git a/drivers/rtc/rtc-fm33256b.c b/drivers/rtc/rtc-fm33256b.c
new file mode 100644
index 0000000..a9650d4
--- /dev/null
+++ b/drivers/rtc/rtc-fm33256b.c
@@ -0,0 +1,166 @@
+/*
+ * Cypress FM33256B Processor Companion RTC 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+struct fm33256b_rtc {
+ struct fm33256b *fm33256b;
+ struct rtc_device *rtcdev;
+};
+
+static int fm33256b_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ int ret;
+ struct fm33256b_rtc *rtc = dev_get_drvdata(dev);
+ uint8_t time[7];
+
+ /* Lock time update */
+ ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
+ FM33256B_RTC_ALARM_CONTROL_REG,
+ FM33256B_R, FM33256B_R);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_read(rtc->fm33256b->regmap_pc,
+ FM33256B_SECONDS_REG, time, sizeof(time));
+ if (ret < 0)
+ return ret;
+
+ /* Unlock time update */
+ ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
+ FM33256B_RTC_ALARM_CONTROL_REG,
+ FM33256B_R, 0);
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = bcd2bin(time[0]);
+ tm->tm_min = bcd2bin(time[1]);
+ tm->tm_hour = bcd2bin(time[2]);
+ tm->tm_wday = bcd2bin(time[3]) - 1;
+ tm->tm_mday = bcd2bin(time[4]);
+ tm->tm_mon = bcd2bin(time[5]) - 1;
+ tm->tm_year = bcd2bin(time[6]);
+
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+
+ return rtc_valid_tm(tm);
+}
+
+static int fm33256b_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ int ret;
+ struct fm33256b_rtc *rtc = dev_get_drvdata(dev);
+ uint8_t time[7];
+
+ time[0] = bin2bcd(tm->tm_sec);
+ time[1] = bin2bcd(tm->tm_min);
+ time[2] = bin2bcd(tm->tm_hour);
+ time[3] = bin2bcd(tm->tm_wday + 1);
+ time[4] = bin2bcd(tm->tm_mday);
+ time[5] = bin2bcd(tm->tm_mon + 1);
+ time[6] = bin2bcd(tm->tm_year % 100);
+
+ /* Unlock time update */
+ ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
+ FM33256B_RTC_ALARM_CONTROL_REG,
+ FM33256B_W, FM33256B_W);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_write(rtc->fm33256b->regmap_pc,
+ FM33256B_SECONDS_REG, time, sizeof(time));
+ if (ret < 0)
+ return ret;
+
+ /* Lock time update */
+ ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
+ FM33256B_RTC_ALARM_CONTROL_REG,
+ FM33256B_W, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct rtc_class_ops fm33256b_rtc_ops = {
+ .read_time = fm33256b_rtc_readtime,
+ .set_time = fm33256b_rtc_settime,
+};
+
+static int fm33256b_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fm33256b *fm33256b;
+ struct fm33256b_rtc *rtc;
+
+ fm33256b = dev_get_drvdata(dev->parent);
+
+ rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->fm33256b = fm33256b;
+ rtc->rtcdev = devm_rtc_device_register(&pdev->dev, KBUILD_MODNAME,
+ &fm33256b_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtcdev))
+ return PTR_ERR(rtc->rtcdev);
+
+ return 0;
+}
+
+static int fm33256b_rtc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fm33256b *fm33256b;
+ struct fm33256b_rtc *rtc = platform_get_drvdata(pdev);
+
+ fm33256b = dev_get_drvdata(dev->parent);
+
+ devm_rtc_device_unregister(&pdev->dev, rtc->rtcdev);
+
+ return 0;
+}
+
+static const struct of_device_id fm33256b_rtc_dt_ids[] = {
+ { .compatible = "cypress,fm33256b-rtc" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, fm33256b_rtc_dt_ids);
+
+static struct platform_driver fm33256b_rtc_driver = {
+ .driver = {
+ .name = "fm33256b-rtc",
+ .of_match_table = fm33256b_rtc_dt_ids,
+ },
+ .probe = fm33256b_rtc_probe,
+ .remove = fm33256b_rtc_remove,
+};
+module_platform_driver(fm33256b_rtc_driver);
+
+MODULE_ALIAS("platform:fm33256b-rtc");
+MODULE_AUTHOR("Jeppe Ledet-Pedersen <[email protected]>");
+MODULE_DESCRIPTION("Cypress FM33256B Processor Companion RTC Driver");
+MODULE_LICENSE("GPL v2");
--
2.1.4
Hi,
Looks mostly good, a few comments below
On 20/04/2016 at 13:07:51 +0200, Jeppe Ledet-Pedersen wrote :
Please always include a commit message.
> Signed-off-by: Jeppe Ledet-Pedersen <[email protected]>
> ---
[...]
> +static int fm33256b_rtc_readtime(struct device *dev, struct rtc_time *tm)
> +{
> + int ret;
> + struct fm33256b_rtc *rtc = dev_get_drvdata(dev);
> + uint8_t time[7];
Use u8 here
> +
> + /* Lock time update */
> + ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
> + FM33256B_RTC_ALARM_CONTROL_REG,
> + FM33256B_R, FM33256B_R);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_bulk_read(rtc->fm33256b->regmap_pc,
> + FM33256B_SECONDS_REG, time, sizeof(time));
> + if (ret < 0)
> + return ret;
> +
> + /* Unlock time update */
> + ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
> + FM33256B_RTC_ALARM_CONTROL_REG,
> + FM33256B_R, 0);
> + if (ret < 0)
> + return ret;
> +
> + tm->tm_sec = bcd2bin(time[0]);
> + tm->tm_min = bcd2bin(time[1]);
> + tm->tm_hour = bcd2bin(time[2]);
> + tm->tm_wday = bcd2bin(time[3]) - 1;
> + tm->tm_mday = bcd2bin(time[4]);
> + tm->tm_mon = bcd2bin(time[5]) - 1;
> + tm->tm_year = bcd2bin(time[6]);
> +
> + if (tm->tm_year < 70)
> + tm->tm_year += 100;
> +
I would advise against using that construct. It doesn't work well with
leap years and you probably don't care about dates before 2000.
> + return rtc_valid_tm(tm);
> +}
> +
> +static int fm33256b_rtc_settime(struct device *dev, struct rtc_time *tm)
> +{
> + int ret;
> + struct fm33256b_rtc *rtc = dev_get_drvdata(dev);
> + uint8_t time[7];
> +
u8
> + time[0] = bin2bcd(tm->tm_sec);
> + time[1] = bin2bcd(tm->tm_min);
> + time[2] = bin2bcd(tm->tm_hour);
> + time[3] = bin2bcd(tm->tm_wday + 1);
> + time[4] = bin2bcd(tm->tm_mday);
> + time[5] = bin2bcd(tm->tm_mon + 1);
> + time[6] = bin2bcd(tm->tm_year % 100);
> +
I would also prefer that you explicitly enforce the range of supported
years and return -EINVAL for out of range values.
> + /* Unlock time update */
> + ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
> + FM33256B_RTC_ALARM_CONTROL_REG,
> + FM33256B_W, FM33256B_W);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_bulk_write(rtc->fm33256b->regmap_pc,
> + FM33256B_SECONDS_REG, time, sizeof(time));
> + if (ret < 0)
> + return ret;
> +
> + /* Lock time update */
> + ret = regmap_update_bits(rtc->fm33256b->regmap_pc,
> + FM33256B_RTC_ALARM_CONTROL_REG,
> + FM33256B_W, 0);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static const struct rtc_class_ops fm33256b_rtc_ops = {
> + .read_time = fm33256b_rtc_readtime,
> + .set_time = fm33256b_rtc_settime,
> +};
> +
> +static int fm33256b_rtc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct fm33256b *fm33256b;
> + struct fm33256b_rtc *rtc;
> +
> + fm33256b = dev_get_drvdata(dev->parent);
> +
> + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
> + if (!rtc)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, rtc);
> +
> + rtc->fm33256b = fm33256b;
> + rtc->rtcdev = devm_rtc_device_register(&pdev->dev, KBUILD_MODNAME,
> + &fm33256b_rtc_ops, THIS_MODULE);
> + if (IS_ERR(rtc->rtcdev))
> + return PTR_ERR(rtc->rtcdev);
> +
> + return 0;
You can use PTR_ERR_OR_ZERO
> +}
> +
> +static int fm33256b_rtc_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct fm33256b *fm33256b;
> + struct fm33256b_rtc *rtc = platform_get_drvdata(pdev);
> +
> + fm33256b = dev_get_drvdata(dev->parent);
> +
> + devm_rtc_device_unregister(&pdev->dev, rtc->rtcdev);
> +
> + return 0;
> +}
> +
The whole fm33256b_rtc_remove() is unnecessary.
> +static const struct of_device_id fm33256b_rtc_dt_ids[] = {
> + { .compatible = "cypress,fm33256b-rtc" },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, fm33256b_rtc_dt_ids);
> +
> +static struct platform_driver fm33256b_rtc_driver = {
> + .driver = {
> + .name = "fm33256b-rtc",
> + .of_match_table = fm33256b_rtc_dt_ids,
> + },
> + .probe = fm33256b_rtc_probe,
> + .remove = fm33256b_rtc_remove,
> +};
> +module_platform_driver(fm33256b_rtc_driver);
> +
> +MODULE_ALIAS("platform:fm33256b-rtc");
> +MODULE_AUTHOR("Jeppe Ledet-Pedersen <[email protected]>");
> +MODULE_DESCRIPTION("Cypress FM33256B Processor Companion RTC Driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.4
>
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
Hi,
On 20/04/2016 at 13:07:50 +0200, Jeppe Ledet-Pedersen wrote :
> Signed-off-by: Jeppe Ledet-Pedersen <[email protected]>
> ---
> MAINTAINERS | 1 +
> drivers/misc/eeprom/Kconfig | 12 ++++
> drivers/misc/eeprom/Makefile | 1 +
> drivers/misc/eeprom/fm33256b-fram.c | 110 ++++++++++++++++++++++++++++++++++++
> 4 files changed, 124 insertions(+)
> create mode 100644 drivers/misc/eeprom/fm33256b-fram.c
>
There is now an nvmem framework that will allow you to easily have both
an in-kernel and a userspace interface.
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
Hi,
[auto build test ERROR on ljones-mfd/for-mfd-next]
[also build test ERROR on v4.6-rc4 next-20160421]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/Jeppe-Ledet-Pedersen/Cypress-FM33256B-processor-companion-support/20160420-193004
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
config: x86_64-randconfig-n0-04220648 (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All error/warnings (new ones prefixed by >>):
warning: (BMP085_SPI && TOUCHSCREEN_TSC2005 && SERIAL_MAX310X && SERIAL_SC16IS7XX_SPI && MFD_FM33256B && MFD_DA9052_SPI && MFD_MC13XXX_SPI && MFD_TPS65912_SPI && MFD_ARIZONA_SPI && MFD_WM831X_SPI && SND_SOC && SND_SOC_ADAU1761_SPI && SND_SOC_ADAU1781_SPI && SND_SOC_ADAU1977_SPI && SND_SOC_CS4271_SPI && SND_SOC_PCM3168A_SPI && SND_SOC_PCM512x_SPI && SND_SOC_SSM2602_SPI && SND_SOC_WM8804_SPI && RTC_I2C_AND_SPI && RTC_DRV_DS1343 && RTC_DRV_RX6110 && BMC150_ACCEL_SPI && MMA7455_SPI && AD5380 && BMG160_SPI && AFE4403 && INV_MPU6050_SPI && SENSORS_HMC5843_SPI) selects REGMAP_SPI which has unmet direct dependencies (SPI)
drivers/built-in.o: In function `regmap_spi_read':
>> regmap-spi.c:(.text+0x29e4c9): undefined reference to `spi_write_then_read'
drivers/built-in.o: In function `regmap_spi_gather_write':
>> regmap-spi.c:(.text+0x29e5e9): undefined reference to `spi_sync'
drivers/built-in.o: In function `regmap_spi_write':
regmap-spi.c:(.text+0x29e6d9): undefined reference to `spi_sync'
drivers/built-in.o: In function `regmap_spi_async_write':
>> regmap-spi.c:(.text+0x29e9eb): undefined reference to `spi_async'
drivers/built-in.o: In function `fm33256b_probe':
>> fm33256b.c:(.text+0x35c975): undefined reference to `spi_setup'
drivers/built-in.o: In function `fm33256b_io':
>> fm33256b.c:(.text+0x35cec6): undefined reference to `spi_sync'
fm33256b.c:(.text+0x35cf53): undefined reference to `spi_sync'
drivers/built-in.o: In function `fm33256b_spi_driver_init':
>> fm33256b.c:(.init.text+0x131e9): undefined reference to `__spi_register_driver'
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Wed, Apr 20, 2016 at 01:07:49PM +0200, Jeppe Ledet-Pedersen wrote:
> 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 <[email protected]>
> ---
> 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
What does the driver do if charging is disabled? Would using 'status =
"disabled"' be any different?
> +- cypress,charge-fast: enable fast (1 mA) charging
What does fast mean?
I think it is time for a common binding here. There's all sorts of
variations on setting the charge current in bindings. Add something like
"charge-current-microamp" in power_supply.txt and use it here. Then
1000uA implies "fast charge".
> +
> +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>;
> + };
Where's the 2nd sub device?
> +};
On 22/04/2016 at 14:32:32 -0500, Rob Herring wrote :
> On Wed, Apr 20, 2016 at 01:07:49PM +0200, Jeppe Ledet-Pedersen wrote:
> > 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 <[email protected]>
> > ---
> > 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
>
> What does the driver do if charging is disabled? Would using 'status =
> "disabled"' be any different?
>
Good catch, I didn't look at this patch but it includes a lot of code
that should be going to the RTC driver.
If trickle charging is not enabled, I guess the RTC will not charge its
backup battery.
> > +- cypress,charge-fast: enable fast (1 mA) charging
>
> What does fast mean?
>
> I think it is time for a common binding here. There's all sorts of
> variations on setting the charge current in bindings. Add something like
> "charge-current-microamp" in power_supply.txt and use it here. Then
> 1000uA implies "fast charge".
Well, this is not a power supply, it is an RTC.
I think both properties should got to the RTC subdevice and be parsed in
the RTC driver.
Note that for trickle charging, we currently have the following
properties:
- trickle-resistor-ohms
- trickle-diode-disable
- abracon,tc-diode
- abracon,tc-resistor
abracon,tc-resistor can be replaced by trickle-resistor-ohms, I'll make
a patch but abracon,tc-diode allows to select a diode type (and in
particular a voltage drop).
I think we could add a trickle-current-microamp like you suggested and
trickle-diode-disable can be reused.
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
On 22/04/16 01:44, Alexandre Belloni wrote:
> Hi,
Hi Alexandre,
> Looks mostly good, a few comments below
Thanks for the review. I'll add your fixes and send a v2 patch.
-Jeppe
On 22/04/16 22:11, Alexandre Belloni wrote:
> On 22/04/2016 at 14:32:32 -0500, Rob Herring wrote :
>> On Wed, Apr 20, 2016 at 01:07:49PM +0200, Jeppe Ledet-Pedersen wrote:
>>> 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 <[email protected]>
>>> ---
>>> 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
>>
>> What does the driver do if charging is disabled? Would using 'status =
>> "disabled"' be any different?
>>
>
> Good catch, I didn't look at this patch but it includes a lot of code
> that should be going to the RTC driver.
> If trickle charging is not enabled, I guess the RTC will not charge its
> backup battery.
Thank you for the comments.
Alexandre is correct. If the "cypress,charge-enabled" property is
present, the internal 80 uA trickle charger is enabled to charge a
backup capacitor on the VBAK pin.
Do you want more code than the trickle charger setup moved to the RTC
driver?
>>> +- cypress,charge-fast: enable fast (1 mA) charging
>>
>> What does fast mean?
>>
Fast charging means charging with 1 mA instead of 80 uA. The wording is
from the datasheet, but I agree it should be renamed to something that
better explains what the difference is.
>> I think it is time for a common binding here. There's all sorts of
>> variations on setting the charge current in bindings. Add something like
>> "charge-current-microamp" in power_supply.txt and use it here. Then
>> 1000uA implies "fast charge".
>
> Well, this is not a power supply, it is an RTC.
>
> I think both properties should got to the RTC subdevice and be parsed in
> the RTC driver.
The backup capacitor is primarily used to supply current to the RTC, but
a couple of other registers are also battery-backed (watchdog reset
cause, event counter), so I put the charger setup in the main MFD file.
If you prefer, I can move them both to the RTC driver?
> Note that for trickle charging, we currently have the following
> properties:
> - trickle-resistor-ohms
> - trickle-diode-disable
> - abracon,tc-diode
> - abracon,tc-resistor
>
> abracon,tc-resistor can be replaced by trickle-resistor-ohms, I'll make
> a patch but abracon,tc-diode allows to select a diode type (and in
> particular a voltage drop).
>
> I think we could add a trickle-current-microamp like you suggested and
> trickle-diode-disable can be reused.
So replace "cypress,charge-enabled/charge-fast" with
"trickle-current-microamp" and a check for 0/80/1000 uA?
Thanks,
-Jeppe
On 22/04/16 21:32, Rob Herring wrote:
> On Wed, Apr 20, 2016 at 01:07:49PM +0200, Jeppe Ledet-Pedersen wrote:
[snip]
>> +
>> +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>;
>> + };
>
> Where's the 2nd sub device?
Hi Rob,
Right now I just add the two sub-devices using mfd_add_devices in the
fm33256b_probe function in my MFD driver. Would it be better to check
for compatible child nodes before adding each sub-device, like it's done
in drivers/mfd/tc3589x.c?
Thanks,
-Jeppe
On 22/04/16 01:54, Alexandre Belloni wrote:
> Hi,
Hi Alexandre,
> On 20/04/2016 at 13:07:50 +0200, Jeppe Ledet-Pedersen wrote :
>> Signed-off-by: Jeppe Ledet-Pedersen <[email protected]>
>> ---
>> MAINTAINERS | 1 +
>> drivers/misc/eeprom/Kconfig | 12 ++++
>> drivers/misc/eeprom/Makefile | 1 +
>> drivers/misc/eeprom/fm33256b-fram.c | 110 ++++++++++++++++++++++++++++++++++++
>> 4 files changed, 124 insertions(+)
>> create mode 100644 drivers/misc/eeprom/fm33256b-fram.c
>>
>
> There is now an nvmem framework that will allow you to easily have both
> an in-kernel and a userspace interface.
Thanks, I'll take a look at nvmem. It does look like a much better fit.
-Jeppe