[PATCH 3/3] mfd: d1w: iproc: Add d1w driver

Scott Branden scott.branden at broadcom.com
Wed Aug 16 10:45:25 PDT 2017


From: Shreesha Rajashekar <shreesha at broadcom.com>

d1w bus master controller is added as a mfd node.
ds1wm driver is hooked to this node through the mfd framework.

Signed-off-by: Shreesha Rajashekar <shreesha at broadcom.com>
Signed-off-by: Scott Branden <scott.branden at broadcom.com>
---
 drivers/mfd/Kconfig         |  11 +++
 drivers/mfd/Makefile        |   1 +
 drivers/mfd/bcm-iproc-d1w.c | 202 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 drivers/mfd/bcm-iproc-d1w.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 94ad2c1..a7d9335 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -216,6 +216,17 @@ config MFD_ASIC3
 	  This driver supports the ASIC3 multifunction chip found on many
 	  PDAs (mainly iPAQ and HTC based ones)
 
+config MFD_IPROC_D1W
+	bool "IPROC DS1WM one wire interface"
+	depends on (ARCH_BCM_IPROC || COMPILE_TEST)
+	select MFD_CORE
+	default ARCH_BCM_IPROC
+	help
+	  This driver provides support to the Dallas One Wire (D1W).This block
+	  is found in various Broadcom iProc family of SoCs.
+	  The Cygnus Dallas 1-Wire Interface provides complete
+	  control of the 1-Wire bus through 8-bit commands.
+
 config PMIC_DA903X
 	bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
 	depends on I2C=y
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 080793b..2f39fdd 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -217,6 +217,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC)	+= intel_soc_pmic_bxtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC)	+= intel_soc_pmic_chtwc.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
+obj-$(CONFIG_MFD_IPROC_D1W)	+= bcm-iproc-d1w.o
 
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
 obj-$(CONFIG_MFD_SUN4I_GPADC)	+= sun4i-gpadc.o
diff --git a/drivers/mfd/bcm-iproc-d1w.c b/drivers/mfd/bcm-iproc-d1w.c
new file mode 100644
index 0000000..d439060
--- /dev/null
+++ b/drivers/mfd/bcm-iproc-d1w.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 Broadcom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/stddef.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ds1wm.h>
+
+#define D1W_DIN_OFFSET  0x00
+#define D1W_DOUT_OFFSET 0x04
+#define D1W_ADDR_OFFSET 0x08
+#define D1W_CTRL_OFFSET 0x0C
+
+#define WRITE_CMD 0x3
+#define READ_CMD 0x2
+
+#define DEFAULT_RESET_RECOVERY_DELAY	1
+/* CLOCK_RATE programmed in the IP divisor register DS1WM_CLKDIV*/
+#define DEFAULT_CLOCK_RATE		100000000
+
+struct iproc_ds1wm_core {
+	struct ds1wm_driver_data plat_data;
+	struct clk *ds1wm_clk;
+};
+
+static void iproc_d1w_write_register(void __iomem *base, u32 reg,
+		u8 val)
+{
+	writel(val, base + D1W_DIN_OFFSET);
+	writel(reg, base + D1W_ADDR_OFFSET);
+	writel(WRITE_CMD, base + D1W_CTRL_OFFSET);
+}
+
+static u8 iproc_d1w_read_register(void __iomem *base, u32 reg)
+{
+	writel(reg, base + D1W_ADDR_OFFSET);
+	writel(READ_CMD, base + D1W_CTRL_OFFSET);
+	return readb(base + D1W_DOUT_OFFSET);
+}
+
+static int iproc_ds1wm_enable(struct platform_device *pdev)
+{
+	struct iproc_ds1wm_core *ds1wm_core;
+
+	ds1wm_core = dev_get_drvdata(pdev->dev.parent);
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	if (ds1wm_core->ds1wm_clk)
+		return clk_prepare_enable(ds1wm_core->ds1wm_clk);
+
+	return 0;
+}
+
+static int iproc_ds1wm_disable(struct platform_device *pdev)
+{
+	struct iproc_ds1wm_core *ds1wm_core;
+
+	ds1wm_core = dev_get_drvdata(pdev->dev.parent);
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+	if (ds1wm_core->ds1wm_clk)
+		clk_disable_unprepare(ds1wm_core->ds1wm_clk);
+
+	return 0;
+}
+
+static struct resource iproc_ds1wm_resources[] = {
+	[0] = {
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags = IORESOURCE_IRQ,
+	}
+};
+
+static struct mfd_cell iproc_ds1wm_mfd_cell = {
+	.name          = "ds1wm",
+	.enable        = iproc_ds1wm_enable,
+	.disable       = iproc_ds1wm_disable,
+	.num_resources = ARRAY_SIZE(iproc_ds1wm_resources),
+	.resources     = iproc_ds1wm_resources,
+};
+
+static int iproc_d1w_dt_binding(struct platform_device *pdev,
+		struct iproc_ds1wm_core *ds1wm_core)
+{
+	int ret;
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"reset-recover-delay",
+			&ds1wm_core->plat_data.reset_recover_delay);
+	if (ret < 0)
+		ds1wm_core->plat_data.reset_recover_delay =
+				DEFAULT_RESET_RECOVERY_DELAY;
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"clock-frequency", &ds1wm_core->plat_data.clock_rate);
+	if (ret < 0)
+		ds1wm_core->plat_data.clock_rate = DEFAULT_CLOCK_RATE;
+
+	ds1wm_core->ds1wm_clk = devm_clk_get(&pdev->dev, "iproc_d1w_clk");
+	if (IS_ERR(ds1wm_core->ds1wm_clk)) {
+		dev_info(&pdev->dev,
+				"No clock specified. Assuming it's enabled\n");
+		ds1wm_core->ds1wm_clk = NULL;
+	}
+	ret = clk_set_rate(ds1wm_core->ds1wm_clk,
+		ds1wm_core->plat_data.clock_rate);
+	if (ret)
+		dev_info(&pdev->dev,
+		"Failed to set to %u\n", ds1wm_core->plat_data.clock_rate);
+	return ret;
+}
+
+static int iproc_d1w_mfd_init(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+	int irq;
+	struct iproc_ds1wm_core *ds1wm_core;
+
+	ds1wm_core = platform_get_drvdata(pdev);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No Memory Resource specified\n");
+		return -ENODEV;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "No IRQ specified\n");
+		return irq;
+	}
+
+	ds1wm_core->plat_data.read = iproc_d1w_read_register;
+	ds1wm_core->plat_data.write = iproc_d1w_write_register;
+
+	ret = mfd_add_devices(&pdev->dev, pdev->id,
+			      &iproc_ds1wm_mfd_cell, 1, res, irq, NULL);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register iproc_d1w mfd\n");
+
+	dev_dbg(&pdev->dev, "Added iproc_d1w mfd successfully\n");
+	return ret;
+}
+
+static int iproc_d1w_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct iproc_ds1wm_core *ds1wm_core;
+
+	ds1wm_core = devm_kzalloc(&pdev->dev,
+			sizeof(struct iproc_ds1wm_core), GFP_KERNEL);
+	if (!ds1wm_core)
+		return -ENOMEM;
+
+	ret = iproc_d1w_dt_binding(pdev, ds1wm_core);
+	if (ret) {
+		dev_err(&pdev->dev, "Probe failed\n");
+		return ret;
+	}
+
+	iproc_ds1wm_mfd_cell.platform_data = &ds1wm_core->plat_data;
+	iproc_ds1wm_mfd_cell.pdata_size = sizeof(struct ds1wm_driver_data);
+
+	platform_set_drvdata(pdev, ds1wm_core);
+	ret = iproc_d1w_mfd_init(pdev);
+
+	return ret;
+}
+
+static const struct of_device_id iproc_d1w_of_match[] = {
+	{ .compatible = "brcm,iproc-d1w" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, iproc_d1w_of_match);
+
+static struct platform_driver iproc_d1w_driver = {
+	.driver = {
+		.name = "brcm,iproc-d1w",
+		.of_match_table = of_match_ptr(iproc_d1w_of_match),
+	},
+	.probe = iproc_d1w_probe,
+};
+module_platform_driver(iproc_d1w_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Broadcom one wire busmaster Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.5.0




More information about the linux-arm-kernel mailing list