summaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-designware-platdrv.c
diff options
context:
space:
mode:
authorSerge Semin <Sergey.Semin@baikalelectronics.ru>2020-05-28 11:33:21 +0200
committerWolfram Sang <wsa@kernel.org>2020-05-30 23:03:06 +0200
commitfcb82a939df86018641f38124cb9a7811a5f8505 (patch)
treef9b3d40e9ab2537066b03165c6c31a37bae6b1a2 /drivers/i2c/busses/i2c-designware-platdrv.c
parenti2c: designware: Move reg-space remapping into a dedicated function (diff)
downloadlinux-fcb82a939df86018641f38124cb9a7811a5f8505.tar.xz
linux-fcb82a939df86018641f38124cb9a7811a5f8505.zip
i2c: designware: Add Baikal-T1 System I2C support
Baikal-T1 System Controller is equipped with a dedicated I2C Controller which functionality is based on the DW APB I2C IP-core, the only difference in a way it' registers are accessed. There are three access register provided in the System Controller registers map, which indirectly address the normal DW APB I2C registers space. So in order to have the Baikal-T1 System I2C Controller supported by the common DW APB I2C driver we created a dedicated Dw I2C controller model quirk, which retrieves the syscon regmap from the parental dt node and creates a new regmap based on it. Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Wolfram Sang <wsa@kernel.org>
Diffstat (limited to 'drivers/i2c/busses/i2c-designware-platdrv.c')
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c78
1 files changed, 76 insertions, 2 deletions
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 9d467fa0e163..b55c730f28cf 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_data/i2c-designware.h>
@@ -25,6 +26,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -58,6 +60,63 @@ MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
#endif
#ifdef CONFIG_OF
+#define BT1_I2C_CTL 0x100
+#define BT1_I2C_CTL_ADDR_MASK GENMASK(7, 0)
+#define BT1_I2C_CTL_WR BIT(8)
+#define BT1_I2C_CTL_GO BIT(31)
+#define BT1_I2C_DI 0x104
+#define BT1_I2C_DO 0x108
+
+static int bt1_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct dw_i2c_dev *dev = context;
+ int ret;
+
+ /*
+ * Note these methods shouldn't ever fail because the system controller
+ * registers are memory mapped. We check the return value just in case.
+ */
+ ret = regmap_write(dev->sysmap, BT1_I2C_CTL,
+ BT1_I2C_CTL_GO | (reg & BT1_I2C_CTL_ADDR_MASK));
+ if (ret)
+ return ret;
+
+ return regmap_read(dev->sysmap, BT1_I2C_DO, val);
+}
+
+static int bt1_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct dw_i2c_dev *dev = context;
+ int ret;
+
+ ret = regmap_write(dev->sysmap, BT1_I2C_DI, val);
+ if (ret)
+ return ret;
+
+ return regmap_write(dev->sysmap, BT1_I2C_CTL,
+ BT1_I2C_CTL_GO | BT1_I2C_CTL_WR | (reg & BT1_I2C_CTL_ADDR_MASK));
+}
+
+static struct regmap_config bt1_i2c_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+ .reg_read = bt1_i2c_read,
+ .reg_write = bt1_i2c_write,
+ .max_register = DW_IC_COMP_TYPE,
+};
+
+static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
+{
+ dev->sysmap = syscon_node_to_regmap(dev->dev->of_node->parent);
+ if (IS_ERR(dev->sysmap))
+ return PTR_ERR(dev->sysmap);
+
+ dev->map = devm_regmap_init(dev->dev, NULL, dev, &bt1_i2c_cfg);
+ return PTR_ERR_OR_ZERO(dev->map);
+}
+
#define MSCC_ICPU_CFG_TWI_DELAY 0x0
#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0)
#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4
@@ -90,10 +149,16 @@ static int dw_i2c_of_configure(struct platform_device *pdev)
static const struct of_device_id dw_i2c_of_match[] = {
{ .compatible = "snps,designware-i2c", },
{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
+ { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
{},
};
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
#else
+static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
+{
+ return -ENODEV;
+}
+
static inline int dw_i2c_of_configure(struct platform_device *pdev)
{
return -ENODEV;
@@ -111,10 +176,19 @@ static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev)
static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
{
struct platform_device *pdev = to_platform_device(dev->dev);
+ int ret;
- dev->base = devm_platform_ioremap_resource(pdev, 0);
+ switch (dev->flags & MODEL_MASK) {
+ case MODEL_BAIKAL_BT1:
+ ret = bt1_i2c_request_regs(dev);
+ break;
+ default:
+ dev->base = devm_platform_ioremap_resource(pdev, 0);
+ ret = PTR_ERR_OR_ZERO(dev->base);
+ break;
+ }
- return PTR_ERR_OR_ZERO(dev->base);
+ return ret;
}
static int dw_i2c_plat_probe(struct platform_device *pdev)