Commit fcb82a93 authored by Serge Semin's avatar Serge Semin Committed by Wolfram Sang

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: default avatarSerge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent b7c3d077
...@@ -541,8 +541,9 @@ config I2C_DESIGNWARE_SLAVE ...@@ -541,8 +541,9 @@ config I2C_DESIGNWARE_SLAVE
config I2C_DESIGNWARE_PLATFORM config I2C_DESIGNWARE_PLATFORM
tristate "Synopsys DesignWare Platform" tristate "Synopsys DesignWare Platform"
select I2C_DESIGNWARE_CORE
depends on (ACPI && COMMON_CLK) || !ACPI depends on (ACPI && COMMON_CLK) || !ACPI
select I2C_DESIGNWARE_CORE
select MFD_SYSCON if MIPS_BAIKAL_T1
help help
If you say yes to this option, support will be included for the If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Synopsys DesignWare I2C adapter.
......
...@@ -183,6 +183,7 @@ struct reset_control; ...@@ -183,6 +183,7 @@ struct reset_control;
* struct dw_i2c_dev - private i2c-designware data * struct dw_i2c_dev - private i2c-designware data
* @dev: driver model device node * @dev: driver model device node
* @map: IO registers map * @map: IO registers map
* @sysmap: System controller registers map
* @base: IO registers pointer * @base: IO registers pointer
* @ext: Extended IO registers pointer * @ext: Extended IO registers pointer
* @cmd_complete: tx completion indicator * @cmd_complete: tx completion indicator
...@@ -235,6 +236,7 @@ struct reset_control; ...@@ -235,6 +236,7 @@ struct reset_control;
struct dw_i2c_dev { struct dw_i2c_dev {
struct device *dev; struct device *dev;
struct regmap *map; struct regmap *map;
struct regmap *sysmap;
void __iomem *base; void __iomem *base;
void __iomem *ext; void __iomem *ext;
struct completion cmd_complete; struct completion cmd_complete;
...@@ -290,6 +292,7 @@ struct dw_i2c_dev { ...@@ -290,6 +292,7 @@ struct dw_i2c_dev {
#define ACCESS_NO_IRQ_SUSPEND 0x00000002 #define ACCESS_NO_IRQ_SUSPEND 0x00000002
#define MODEL_MSCC_OCELOT 0x00000100 #define MODEL_MSCC_OCELOT 0x00000100
#define MODEL_BAIKAL_BT1 0x00000200
#define MODEL_MASK 0x00000f00 #define MODEL_MASK 0x00000f00
int i2c_dw_init_regmap(struct dw_i2c_dev *dev); int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_data/i2c-designware.h> #include <linux/platform_data/i2c-designware.h>
...@@ -25,6 +26,7 @@ ...@@ -25,6 +26,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -58,6 +60,63 @@ MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); ...@@ -58,6 +60,63 @@ MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
#endif #endif
#ifdef CONFIG_OF #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 0x0
#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0) #define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0)
#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4 #define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4
...@@ -90,10 +149,16 @@ static int dw_i2c_of_configure(struct platform_device *pdev) ...@@ -90,10 +149,16 @@ static int dw_i2c_of_configure(struct platform_device *pdev)
static const struct of_device_id dw_i2c_of_match[] = { static const struct of_device_id dw_i2c_of_match[] = {
{ .compatible = "snps,designware-i2c", }, { .compatible = "snps,designware-i2c", },
{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT }, { .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); MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
#else #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) static inline int dw_i2c_of_configure(struct platform_device *pdev)
{ {
return -ENODEV; return -ENODEV;
...@@ -111,10 +176,19 @@ static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev) ...@@ -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) static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
{ {
struct platform_device *pdev = to_platform_device(dev->dev); struct platform_device *pdev = to_platform_device(dev->dev);
int ret;
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); 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) static int dw_i2c_plat_probe(struct platform_device *pdev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment