summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-brcmstb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-brcmstb.c')
-rw-r--r--drivers/i2c/busses/i2c-brcmstb.c157
1 files changed, 87 insertions, 70 deletions
diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c
index 78792b4d6437..5fa30e8926c5 100644
--- a/drivers/i2c/busses/i2c-brcmstb.c
+++ b/drivers/i2c/busses/i2c-brcmstb.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2014 Broadcom Corporation
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014 Broadcom Corporation
#include <linux/clk.h>
#include <linux/delay.h>
@@ -22,7 +12,6 @@
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/version.h>
#define N_DATA_REGS 8
@@ -78,7 +67,7 @@
/* BSC block register map structure to cache fields to be written */
struct bsc_regs {
- u32 chip_address; /* slave address */
+ u32 chip_address; /* target address */
u32 data_in[N_DATA_REGS]; /* tx data buffer*/
u32 cnt_reg; /* rx/tx data length */
u32 ctl_reg; /* control register */
@@ -165,14 +154,13 @@ static const struct bsc_clk_param bsc_clk[] = {
struct brcmstb_i2c_dev {
struct device *device;
void __iomem *base;
- void __iomem *irq_base;
int irq;
struct bsc_regs *bsc_regmap;
struct i2c_adapter adapter;
struct completion done;
- bool is_suspended;
u32 clk_freq_hz;
int data_regsz;
+ bool atomic;
};
/* register accessors for both be and le cpu arch */
@@ -253,7 +241,7 @@ static int brcmstb_i2c_wait_for_completion(struct brcmstb_i2c_dev *dev)
int ret = 0;
unsigned long timeout = msecs_to_jiffies(I2C_TIMEOUT);
- if (dev->irq >= 0) {
+ if (dev->irq >= 0 && !dev->atomic) {
if (!wait_for_completion_timeout(&dev->done, timeout))
ret = -ETIMEDOUT;
} else {
@@ -300,7 +288,7 @@ static int brcmstb_send_i2c_cmd(struct brcmstb_i2c_dev *dev,
return rc;
/* only if we are in interrupt mode */
- if (dev->irq >= 0)
+ if (dev->irq >= 0 && !dev->atomic)
reinit_completion(&dev->done);
/* enable BSC CTL interrupt line */
@@ -318,7 +306,7 @@ static int brcmstb_send_i2c_cmd(struct brcmstb_i2c_dev *dev,
goto cmd_out;
}
- if ((CMD_RD || CMD_WR) &&
+ if ((cmd == CMD_RD || cmd == CMD_WR) &&
bsc_readl(dev, iic_enable) & BSC_IIC_EN_NOACK_MASK) {
rc = -EREMOTEIO;
dev_dbg(dev->device, "controller received NOACK intr for %s\n",
@@ -332,7 +320,7 @@ cmd_out:
return rc;
}
-/* Actual data transfer through the BSC master */
+/* Actual data transfer through the BSC controller */
static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
u8 *buf, unsigned int len,
struct i2c_msg *pmsg)
@@ -426,23 +414,22 @@ static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev,
if (msg->flags & I2C_M_TEN) {
/* First byte is 11110XX0 where XX is upper 2 bits */
- addr = 0xF0 | ((msg->addr & 0x300) >> 7);
+ addr = i2c_10bit_addr_hi_from_msg(msg) & ~I2C_M_RD;
bsc_writel(dev, addr, chip_address);
/* Second byte is the remaining 8 bits */
- addr = msg->addr & 0xFF;
+ addr = i2c_10bit_addr_lo_from_msg(msg);
if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0)
return -EREMOTEIO;
if (msg->flags & I2C_M_RD) {
/* For read, send restart without stop condition */
- brcmstb_set_i2c_start_stop(dev, COND_RESTART
- | COND_NOSTOP);
+ brcmstb_set_i2c_start_stop(dev, COND_RESTART | COND_NOSTOP);
+
/* Then re-send the first byte with the read bit set */
- addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01;
+ addr = i2c_10bit_addr_hi_from_msg(msg);
if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0)
return -EREMOTEIO;
-
}
} else {
addr = i2c_8bit_addr_from_msg(msg);
@@ -453,7 +440,6 @@ static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev,
return 0;
}
-/* Master transfer function */
static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg msgs[], int num)
{
@@ -467,9 +453,6 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
int xfersz = brcmstb_i2c_get_xfersz(dev);
u32 cond, cond_per_msg;
- if (dev->is_suspended)
- return -EBUSY;
-
/* Loop through all messages */
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
@@ -488,7 +471,7 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
brcmstb_set_i2c_start_stop(dev, cond);
- /* Send slave address */
+ /* Send target address */
if (!(pmsg->flags & I2C_M_NOSTART)) {
rc = brcmstb_i2c_do_addr(dev, pmsg);
if (rc < 0) {
@@ -536,6 +519,23 @@ out:
}
+static int brcmstb_i2c_xfer_atomic(struct i2c_adapter *adapter,
+ struct i2c_msg msgs[], int num)
+{
+ struct brcmstb_i2c_dev *dev = i2c_get_adapdata(adapter);
+ int ret;
+
+ if (dev->irq >= 0)
+ disable_irq(dev->irq);
+ dev->atomic = true;
+ ret = brcmstb_i2c_xfer(adapter, msgs, num);
+ dev->atomic = false;
+ if (dev->irq >= 0)
+ enable_irq(dev->irq);
+
+ return ret;
+}
+
static u32 brcmstb_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR
@@ -543,7 +543,8 @@ static u32 brcmstb_i2c_functionality(struct i2c_adapter *adap)
}
static const struct i2c_algorithm brcmstb_i2c_algo = {
- .master_xfer = brcmstb_i2c_xfer,
+ .xfer = brcmstb_i2c_xfer,
+ .xfer_atomic = brcmstb_i2c_xfer_atomic,
.functionality = brcmstb_i2c_functionality,
};
@@ -585,13 +586,35 @@ static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev)
brcmstb_i2c_set_bus_speed(dev);
}
+#define AUTOI2C_CTRL0 0x26c
+#define AUTOI2C_CTRL0_RELEASE_BSC BIT(1)
+
+static int bcm2711_release_bsc(struct brcmstb_i2c_dev *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev->device);
+ void __iomem *autoi2c;
+
+ /* Map hardware registers */
+ autoi2c = devm_platform_ioremap_resource_byname(pdev, "auto-i2c");
+ if (IS_ERR(autoi2c))
+ return PTR_ERR(autoi2c);
+
+ writel(AUTOI2C_CTRL0_RELEASE_BSC, autoi2c + AUTOI2C_CTRL0);
+ devm_iounmap(&pdev->dev, autoi2c);
+
+ /* We need to reset the controller after the release */
+ dev->bsc_regmap->iic_enable = 0;
+ bsc_writel(dev, dev->bsc_regmap->iic_enable, iic_enable);
+
+ return 0;
+}
+
static int brcmstb_i2c_probe(struct platform_device *pdev)
{
- int rc = 0;
struct brcmstb_i2c_dev *dev;
struct i2c_adapter *adap;
- struct resource *iomem;
const char *int_name;
+ int rc;
/* Allocate memory for private data structure */
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -607,11 +630,15 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
init_completion(&dev->done);
/* Map hardware registers */
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->base = devm_ioremap_resource(dev->device, iomem);
- if (IS_ERR(dev->base)) {
- rc = -ENOMEM;
- goto probe_errorout;
+ dev->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dev->base))
+ return PTR_ERR(dev->base);
+
+ if (of_device_is_compatible(dev->device->of_node,
+ "brcm,bcm2711-hdmi-i2c")) {
+ rc = bcm2711_release_bsc(dev);
+ if (rc)
+ return rc;
}
rc = of_property_read_string(dev->device->of_node, "interrupt-names",
@@ -620,20 +647,22 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
int_name = NULL;
/* Get the interrupt number */
- dev->irq = platform_get_irq(pdev, 0);
+ dev->irq = platform_get_irq_optional(pdev, 0);
/* disable the bsc interrupt line */
brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE);
/* register the ISR handler */
- rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr,
- IRQF_SHARED,
- int_name ? int_name : pdev->name,
- dev);
-
- if (rc) {
- dev_dbg(dev->device, "falling back to polling mode");
- dev->irq = -1;
+ if (dev->irq >= 0) {
+ rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr,
+ IRQF_SHARED,
+ int_name ? int_name : pdev->name,
+ dev);
+
+ if (rc) {
+ dev_dbg(dev->device, "falling back to polling mode");
+ dev->irq = -1;
+ }
}
if (of_property_read_u32(dev->device->of_node,
@@ -645,7 +674,7 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
/* set the data in/out register size for compatible SoCs */
if (of_device_is_compatible(dev->device->of_node,
- "brcmstb,brcmper-i2c"))
+ "brcm,brcmper-i2c"))
dev->data_regsz = sizeof(u8);
else
dev->data_regsz = sizeof(u32);
@@ -656,43 +685,33 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
- strlcpy(adap->name, "Broadcom STB : ", sizeof(adap->name));
- if (int_name)
- strlcat(adap->name, int_name, sizeof(adap->name));
+ strscpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
adap->algo = &brcmstb_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
rc = i2c_add_adapter(adap);
if (rc)
- goto probe_errorout;
+ return rc;
dev_info(dev->device, "%s@%dhz registered in %s mode\n",
int_name ? int_name : " ", dev->clk_freq_hz,
(dev->irq >= 0) ? "interrupt" : "polling");
return 0;
-
-probe_errorout:
- return rc;
}
-static int brcmstb_i2c_remove(struct platform_device *pdev)
+static void brcmstb_i2c_remove(struct platform_device *pdev)
{
struct brcmstb_i2c_dev *dev = platform_get_drvdata(pdev);
i2c_del_adapter(&dev->adapter);
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int brcmstb_i2c_suspend(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_adapter(&i2c_dev->adapter);
- i2c_dev->is_suspended = true;
- i2c_unlock_adapter(&i2c_dev->adapter);
-
+ i2c_mark_adapter_suspended(&i2c_dev->adapter);
return 0;
}
@@ -700,21 +719,19 @@ static int brcmstb_i2c_resume(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_adapter(&i2c_dev->adapter);
brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
- i2c_dev->is_suspended = false;
- i2c_unlock_adapter(&i2c_dev->adapter);
+ i2c_mark_adapter_resumed(&i2c_dev->adapter);
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend,
- brcmstb_i2c_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend,
+ brcmstb_i2c_resume);
static const struct of_device_id brcmstb_i2c_of_match[] = {
{.compatible = "brcm,brcmstb-i2c"},
{.compatible = "brcm,brcmper-i2c"},
+ {.compatible = "brcm,bcm2711-hdmi-i2c"},
{},
};
MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match);
@@ -723,7 +740,7 @@ static struct platform_driver brcmstb_i2c_driver = {
.driver = {
.name = "brcmstb-i2c",
.of_match_table = brcmstb_i2c_of_match,
- .pm = &brcmstb_i2c_pm,
+ .pm = pm_sleep_ptr(&brcmstb_i2c_pm),
},
.probe = brcmstb_i2c_probe,
.remove = brcmstb_i2c_remove,