mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 23:36:23 +09:00
Merge branch 'i2c/next' into next
* i2c/next: (28 commits) LF-263-2 i2c: imx: increase PM timeout to avoid operate clk frequently LF-263-1 i2c: lpi2c: increase PM timeout to avoid operate clk frequently i2c: mux: pca954x: support property idle-state dt-bindings: i2c: support property idle-state LF-98 i2c: imx: fix the judgement of slave mode in isr ...
This commit is contained in:
commit
a44f25b882
|
@ -25,6 +25,8 @@ Required Properties:
|
|||
Optional Properties:
|
||||
|
||||
- reset-gpios: Reference to the GPIO connected to the reset input.
|
||||
- idle-state: if present, overrides i2c-mux-idle-disconnect,
|
||||
Please refer to Documentation/devicetree/bindings/mux/mux-controller.txt
|
||||
- i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
|
||||
children in idle state. This is necessary for example, if there are several
|
||||
multiplexers on the bus and the devices behind them use same I2C addresses.
|
||||
|
|
29
Documentation/devicetree/bindings/i2c/i2c-rpmsg-imx.txt
Normal file
29
Documentation/devicetree/bindings/i2c/i2c-rpmsg-imx.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
* Freescale Virtual I2C RPMSG bus driver for i.MX
|
||||
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "fsl,i2c-rpbus" for I2C bus over RPMSG compatible on i.MX8QXP/QM soc
|
||||
The i2c-rpbus node should define its bus id (which is the node communicating
|
||||
with M4) in alias.
|
||||
|
||||
Examples:
|
||||
|
||||
aliases {
|
||||
...
|
||||
i2c1 = &i2c_rpbus_1;
|
||||
...
|
||||
};
|
||||
|
||||
&i2c_rpbus_1 {
|
||||
compatible = "fsl,i2c-rpbus";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
|
||||
devs_in_this_i2c_bus__for_example: pca6416@20 {
|
||||
compatible = "ti,tca6416";
|
||||
reg = <0x20>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
};
|
|
@ -922,6 +922,12 @@ config I2C_RK3X
|
|||
This driver can also be built as a module. If so, the module will
|
||||
be called i2c-rk3x.
|
||||
|
||||
config I2C_RPBUS
|
||||
tristate "I2C proxy bus over RPMSG"
|
||||
depends on I2C && RPMSG
|
||||
help
|
||||
This driver can support virtual i2c-rpmsg function.
|
||||
|
||||
config HAVE_S3C2410_I2C
|
||||
bool
|
||||
help
|
||||
|
|
|
@ -130,6 +130,7 @@ obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
|
|||
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
|
||||
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
|
||||
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
|
||||
obj-$(CONFIG_I2C_RPBUS) += i2c-rpmsg-imx.o
|
||||
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
|
||||
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
|
||||
obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
#define FAST_PLUS_MAX_BITRATE 3400000
|
||||
#define HIGHSPEED_MAX_BITRATE 5000000
|
||||
|
||||
#define I2C_PM_TIMEOUT 10 /* ms */
|
||||
#define I2C_PM_TIMEOUT 1000 /* ms */
|
||||
|
||||
enum lpi2c_imx_mode {
|
||||
STANDARD, /* 100+Kbps */
|
||||
|
@ -100,7 +100,9 @@ enum lpi2c_imx_pincfg {
|
|||
|
||||
struct lpi2c_imx_struct {
|
||||
struct i2c_adapter adapter;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
struct clk *clk_per;
|
||||
struct clk *clk_ipg;
|
||||
void __iomem *base;
|
||||
__u8 *rx_buf;
|
||||
__u8 *tx_buf;
|
||||
|
@ -213,7 +215,12 @@ static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
|
|||
|
||||
lpi2c_imx_set_mode(lpi2c_imx);
|
||||
|
||||
clk_rate = clk_get_rate(lpi2c_imx->clk);
|
||||
clk_rate = clk_get_rate(lpi2c_imx->clk_per);
|
||||
if (!clk_rate) {
|
||||
dev_dbg(&lpi2c_imx->adapter.dev, "clk_per rate is 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
|
||||
filt = 0;
|
||||
else
|
||||
|
@ -513,15 +520,17 @@ static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)
|
|||
lpi2c_imx_intctrl(lpi2c_imx, 0);
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MSR);
|
||||
|
||||
if (temp & MSR_NDF) {
|
||||
complete(&lpi2c_imx->complete);
|
||||
goto ret;
|
||||
}
|
||||
|
||||
if (temp & MSR_RDF)
|
||||
lpi2c_imx_read_rxfifo(lpi2c_imx);
|
||||
|
||||
if (temp & MSR_TDF)
|
||||
else if (temp & MSR_TDF)
|
||||
lpi2c_imx_write_txfifo(lpi2c_imx);
|
||||
|
||||
if (temp & MSR_NDF)
|
||||
complete(&lpi2c_imx->complete);
|
||||
|
||||
ret:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -546,7 +555,7 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct lpi2c_imx_struct *lpi2c_imx;
|
||||
unsigned int temp;
|
||||
int irq, ret;
|
||||
int ret;
|
||||
|
||||
lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx), GFP_KERNEL);
|
||||
if (!lpi2c_imx)
|
||||
|
@ -556,10 +565,10 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(lpi2c_imx->base))
|
||||
return PTR_ERR(lpi2c_imx->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
lpi2c_imx->irq = platform_get_irq(pdev, 0);
|
||||
if (lpi2c_imx->irq < 0) {
|
||||
dev_err(&pdev->dev, "can't get irq number\n");
|
||||
return irq;
|
||||
return lpi2c_imx->irq;
|
||||
}
|
||||
|
||||
lpi2c_imx->adapter.owner = THIS_MODULE;
|
||||
|
@ -569,10 +578,16 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
|
|||
strlcpy(lpi2c_imx->adapter.name, pdev->name,
|
||||
sizeof(lpi2c_imx->adapter.name));
|
||||
|
||||
lpi2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(lpi2c_imx->clk)) {
|
||||
lpi2c_imx->clk_per = devm_clk_get(&pdev->dev, "per");
|
||||
if (IS_ERR(lpi2c_imx->clk_per)) {
|
||||
dev_err(&pdev->dev, "can't get I2C peripheral clock\n");
|
||||
return PTR_ERR(lpi2c_imx->clk);
|
||||
return PTR_ERR(lpi2c_imx->clk_per);
|
||||
}
|
||||
|
||||
lpi2c_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||
if (IS_ERR(lpi2c_imx->clk_ipg)) {
|
||||
dev_err(&pdev->dev, "can't get I2C ipg clock\n");
|
||||
return PTR_ERR(lpi2c_imx->clk_ipg);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
|
@ -580,45 +595,29 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
lpi2c_imx->bitrate = LPI2C_DEFAULT_RATE;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, 0,
|
||||
pdev->name, lpi2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
|
||||
platform_set_drvdata(pdev, lpi2c_imx);
|
||||
|
||||
ret = clk_prepare_enable(lpi2c_imx->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clk enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
temp = readl(lpi2c_imx->base + LPI2C_PARAM);
|
||||
lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
|
||||
lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
ret = i2c_add_adapter(&lpi2c_imx->adapter);
|
||||
if (ret)
|
||||
goto rpm_disable;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n");
|
||||
|
||||
return 0;
|
||||
|
||||
rpm_disable:
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
|
@ -641,8 +640,10 @@ static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(lpi2c_imx->clk);
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
devm_free_irq(dev, lpi2c_imx->irq, lpi2c_imx);
|
||||
clk_disable_unprepare(lpi2c_imx->clk_ipg);
|
||||
clk_disable_unprepare(lpi2c_imx->clk_per);
|
||||
pinctrl_pm_select_idle_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -653,18 +654,50 @@ static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
|
|||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
ret = clk_prepare_enable(lpi2c_imx->clk);
|
||||
ret = clk_prepare_enable(lpi2c_imx->clk_per);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret);
|
||||
dev_err(dev, "can't enable I2C per clock, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(lpi2c_imx->clk_ipg);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(lpi2c_imx->clk_per);
|
||||
dev_err(dev, "can't enable I2C ipg clock, ret=%d\n", ret);
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, lpi2c_imx->irq, lpi2c_imx_isr,
|
||||
IRQF_NO_SUSPEND,
|
||||
dev_name(dev), lpi2c_imx);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't claim irq %d\n", lpi2c_imx->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lpi2c_suspend_noirq(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpi2c_resume_noirq(struct device *dev)
|
||||
{
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops lpi2c_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(lpi2c_suspend_noirq,
|
||||
lpi2c_resume_noirq)
|
||||
SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend,
|
||||
lpi2c_runtime_resume, NULL)
|
||||
};
|
||||
|
@ -679,7 +712,17 @@ static struct platform_driver lpi2c_imx_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
module_platform_driver(lpi2c_imx_driver);
|
||||
static int __init lpi2c_imx_init(void)
|
||||
{
|
||||
return platform_driver_register(&lpi2c_imx_driver);
|
||||
}
|
||||
subsys_initcall(lpi2c_imx_init);
|
||||
|
||||
static void __exit lpi2c_imx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&lpi2c_imx_driver);
|
||||
}
|
||||
module_exit(lpi2c_imx_exit);
|
||||
|
||||
MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
|
||||
MODULE_DESCRIPTION("I2C adapter driver for LPI2C bus");
|
||||
|
|
|
@ -39,18 +39,25 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_data/i2c-imx.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/libata.h>
|
||||
|
||||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "imx-i2c"
|
||||
|
||||
/* Default value */
|
||||
#define IMX_I2C_BIT_RATE 100000 /* 100kHz */
|
||||
#define IMX_I2C_MAX_E_BIT_RATE 384000 /* 384kHz from e7805 errata*/
|
||||
|
||||
/*
|
||||
* Enable DMA if transfer byte size is bigger than this threshold.
|
||||
|
@ -107,8 +114,56 @@
|
|||
#define I2CR_IEN_OPCODE_0 0x0
|
||||
#define I2CR_IEN_OPCODE_1 I2CR_IEN
|
||||
|
||||
#define I2C_PM_TIMEOUT 10 /* ms */
|
||||
#define I2C_PM_TIMEOUT 1000 /* ms */
|
||||
|
||||
enum pinmux_endian_type {
|
||||
BIG_ENDIAN,
|
||||
LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
struct pinmux_cfg {
|
||||
enum pinmux_endian_type endian; /* endian of RCWPMUXCR0 */
|
||||
u32 pmuxcr_offset;
|
||||
u32 pmuxcr_set_bit; /* pin mux of RCWPMUXCR0 */
|
||||
};
|
||||
|
||||
static struct pinmux_cfg ls1012a_pinmux_cfg = {
|
||||
.endian = BIG_ENDIAN,
|
||||
.pmuxcr_offset = 0x430,
|
||||
.pmuxcr_set_bit = 0x10,
|
||||
};
|
||||
|
||||
static struct pinmux_cfg ls1043a_pinmux_cfg = {
|
||||
.endian = BIG_ENDIAN,
|
||||
.pmuxcr_offset = 0x40C,
|
||||
.pmuxcr_set_bit = 0x10,
|
||||
};
|
||||
|
||||
static struct pinmux_cfg ls1046a_pinmux_cfg = {
|
||||
.endian = BIG_ENDIAN,
|
||||
.pmuxcr_offset = 0x40C,
|
||||
.pmuxcr_set_bit = 0x80000000,
|
||||
};
|
||||
|
||||
static const struct of_device_id pinmux_of_match[] = {
|
||||
{ .compatible = "fsl,ls1012a-vf610-i2c", .data = &ls1012a_pinmux_cfg},
|
||||
{ .compatible = "fsl,ls1043a-vf610-i2c", .data = &ls1043a_pinmux_cfg},
|
||||
{ .compatible = "fsl,ls1046a-vf610-i2c", .data = &ls1046a_pinmux_cfg},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pinmux_of_match);
|
||||
|
||||
/* The SCFG, Supplemental Configuration Unit, provides SoC specific
|
||||
* configuration and status registers for the device. There is a
|
||||
* SDHC IO VSEL control register on SCFG for some platforms. It's
|
||||
* used to support SDHC IO voltage switching.
|
||||
*/
|
||||
static const struct of_device_id scfg_device_ids[] = {
|
||||
{ .compatible = "fsl,ls1012a-scfg", },
|
||||
{ .compatible = "fsl,ls1043a-scfg", },
|
||||
{ .compatible = "fsl,ls1046a-scfg", },
|
||||
{}
|
||||
};
|
||||
/*
|
||||
* sorted list of clock divider, register value pairs
|
||||
* taken from table 26-5, p.26-9, Freescale i.MX
|
||||
|
@ -161,6 +216,7 @@ enum imx_i2c_type {
|
|||
IMX1_I2C,
|
||||
IMX21_I2C,
|
||||
VF610_I2C,
|
||||
IMX7D_I2C,
|
||||
};
|
||||
|
||||
struct imx_i2c_hwdata {
|
||||
|
@ -203,6 +259,15 @@ struct imx_i2c_struct {
|
|||
struct pinctrl_state *pinctrl_pins_gpio;
|
||||
|
||||
struct imx_i2c_dma *dma;
|
||||
int layerscape_bus_recover;
|
||||
int gpio;
|
||||
int need_set_pmuxcr;
|
||||
int pmuxcr_set;
|
||||
int pmuxcr_endian;
|
||||
void __iomem *pmuxcr_addr;
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
struct i2c_client *slave;
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||
|
@ -235,6 +300,16 @@ static struct imx_i2c_hwdata vf610_i2c_hwdata = {
|
|||
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata imx7d_i2c_hwdata = {
|
||||
.devtype = IMX7D_I2C,
|
||||
.regshift = IMX_I2C_REGSHIFT,
|
||||
.clk_div = imx_i2c_clk_div,
|
||||
.ndivs = ARRAY_SIZE(imx_i2c_clk_div),
|
||||
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C,
|
||||
.i2cr_ien_opcode = I2CR_IEN_OPCODE_1,
|
||||
|
||||
};
|
||||
|
||||
static const struct platform_device_id imx_i2c_devtype[] = {
|
||||
{
|
||||
.name = "imx1-i2c",
|
||||
|
@ -252,6 +327,7 @@ static const struct of_device_id i2c_imx_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
|
||||
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx7d-i2c", .data = &imx7d_i2c_hwdata, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
|
||||
|
@ -267,6 +343,11 @@ static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx)
|
|||
return i2c_imx->hwdata->devtype == IMX1_I2C;
|
||||
}
|
||||
|
||||
static inline int is_imx7d_i2c(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
return i2c_imx->hwdata->devtype == IMX7D_I2C;
|
||||
}
|
||||
|
||||
static inline void imx_i2c_write_reg(unsigned int val,
|
||||
struct imx_i2c_struct *i2c_imx, unsigned int reg)
|
||||
{
|
||||
|
@ -279,6 +360,14 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
|
|||
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
|
||||
}
|
||||
|
||||
/* Set up i2c controller register and i2c status register to default value. */
|
||||
static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
||||
i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
|
||||
}
|
||||
|
||||
/* Functions for DMA support */
|
||||
static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
||||
dma_addr_t phy_addr)
|
||||
|
@ -414,6 +503,14 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
|
|||
dma->chan_using = NULL;
|
||||
}
|
||||
|
||||
/* Clear arbitration lost bit */
|
||||
static void i2c_imx_clr_al_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
status &= ~I2SR_IAL;
|
||||
status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IAL);
|
||||
imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
|
||||
}
|
||||
|
||||
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
|
||||
{
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
|
@ -426,8 +523,7 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
|
|||
|
||||
/* check for arbitration lost */
|
||||
if (temp & I2SR_IAL) {
|
||||
temp &= ~I2SR_IAL;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
|
||||
i2c_imx_clr_al_bit(temp, i2c_imx);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
|
@ -474,16 +570,24 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
||||
static int i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
||||
unsigned int i2c_clk_rate)
|
||||
{
|
||||
struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
|
||||
unsigned int div;
|
||||
int i;
|
||||
|
||||
/* Divider value calculation */
|
||||
if (i2c_imx->cur_clk == i2c_clk_rate)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Keep the denominator of the following program
|
||||
* always NOT equal to 0.
|
||||
*/
|
||||
|
||||
/* Divider value calculation */
|
||||
if (!(i2c_clk_rate / 2))
|
||||
return -EINVAL;
|
||||
|
||||
i2c_imx->cur_clk = i2c_clk_rate;
|
||||
|
||||
|
@ -514,20 +618,23 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
|||
dev_dbg(&i2c_imx->adapter.dev, "IFDR[IC]=0x%x, REAL DIV=%d\n",
|
||||
i2c_clk_div[i].val, i2c_clk_div[i].div);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_clk_notifier_call(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct imx_i2c_struct *i2c_imx = container_of(nb,
|
||||
struct imx_i2c_struct,
|
||||
clk_change_nb);
|
||||
|
||||
if (action & POST_RATE_CHANGE)
|
||||
i2c_imx_set_clk(i2c_imx, ndata->new_rate);
|
||||
ret = i2c_imx_set_clk(i2c_imx, ndata->new_rate);
|
||||
|
||||
return NOTIFY_OK;
|
||||
return notifier_from_errno(ret);
|
||||
}
|
||||
|
||||
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
||||
|
@ -537,6 +644,10 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
|||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||
|
||||
result = i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
|
||||
/* Enable I2C controller */
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
|
||||
|
@ -588,23 +699,25 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
|
|||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
}
|
||||
|
||||
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
||||
/* Clear interrupt flag bit */
|
||||
static void i2c_imx_clr_if_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_id;
|
||||
unsigned int temp;
|
||||
status &= ~I2SR_IIF;
|
||||
status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
|
||||
imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
|
||||
}
|
||||
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
if (temp & I2SR_IIF) {
|
||||
/* save status register */
|
||||
i2c_imx->i2csr = temp;
|
||||
temp &= ~I2SR_IIF;
|
||||
temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
|
||||
wake_up(&i2c_imx->queue);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
return IRQ_NONE;
|
||||
/* Save status register */
|
||||
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
i2c_imx->i2csr = status | I2SR_IIF;
|
||||
|
||||
wake_up(&i2c_imx->queue);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
||||
|
@ -890,20 +1003,119 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Based on the I2C specification, if the data line (SDA) is
|
||||
* stuck low, the master should send nine * clock pulses.
|
||||
* The I2C slave device that held the bus low should release it
|
||||
* sometime within * those nine clocks. Due to this erratum,
|
||||
* the I2C controller cannot generate nine clock pulses.
|
||||
*/
|
||||
static int i2c_imx_recovery_for_layerscape(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
u32 pmuxcr = 0;
|
||||
int ret;
|
||||
unsigned int i, temp;
|
||||
|
||||
/* configure IICx_SCL/GPIO pin as a GPIO */
|
||||
if (i2c_imx->need_set_pmuxcr == 1) {
|
||||
pmuxcr = ioread32be(i2c_imx->pmuxcr_addr);
|
||||
if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
|
||||
iowrite32be(i2c_imx->pmuxcr_set|pmuxcr,
|
||||
i2c_imx->pmuxcr_addr);
|
||||
else
|
||||
iowrite32(i2c_imx->pmuxcr_set|pmuxcr,
|
||||
i2c_imx->pmuxcr_addr);
|
||||
}
|
||||
|
||||
ret = gpio_request(i2c_imx->gpio, i2c_imx->adapter.name);
|
||||
if (ret) {
|
||||
dev_err(&i2c_imx->adapter.dev,
|
||||
"can't get gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure GPIO pin as an output and open drain. */
|
||||
gpio_direction_output(i2c_imx->gpio, 1);
|
||||
udelay(10);
|
||||
|
||||
/* Write data to generate 9 pulses */
|
||||
for (i = 0; i < 9; i++) {
|
||||
gpio_set_value(i2c_imx->gpio, 1);
|
||||
udelay(10);
|
||||
gpio_set_value(i2c_imx->gpio, 0);
|
||||
udelay(10);
|
||||
}
|
||||
/* ensure that the last level sent is always high */
|
||||
gpio_set_value(i2c_imx->gpio, 1);
|
||||
|
||||
/*
|
||||
* Set I2Cx_IBCR = 0h00 to generate a STOP
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
/*
|
||||
* Set I2Cx_IBCR = 0h80 to reset the I2Cx controller
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode | I2CR_IEN, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
/* Restore the saved value of the register SCFG_RCWPMUXCR0 */
|
||||
if (i2c_imx->need_set_pmuxcr == 1) {
|
||||
if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
|
||||
iowrite32be(pmuxcr, i2c_imx->pmuxcr_addr);
|
||||
else
|
||||
iowrite32(pmuxcr, i2c_imx->pmuxcr_addr);
|
||||
}
|
||||
/*
|
||||
* Set I2C_IBSR[IBAL] to clear the IBAL bit if-
|
||||
* I2C_IBSR[IBAL] = 1
|
||||
*/
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
if (temp & I2SR_IAL)
|
||||
i2c_imx_clr_al_bit(temp, i2c_imx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
unsigned int i, temp;
|
||||
int result;
|
||||
bool is_lastmsg = false;
|
||||
bool enable_runtime_pm = false;
|
||||
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
if (i2c_imx->slave) {
|
||||
dev_err(&i2c_imx->adapter.dev, "Please not do operations of master mode in slave mode");
|
||||
return -EBUSY;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!pm_runtime_enabled(i2c_imx->adapter.dev.parent)) {
|
||||
pm_runtime_enable(i2c_imx->adapter.dev.parent);
|
||||
enable_runtime_pm = true;
|
||||
}
|
||||
|
||||
result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* workround for ERR010027: ensure that the I2C BUS is idle
|
||||
* before switching to master mode and attempting a Start cycle
|
||||
*/
|
||||
result = i2c_imx_bus_busy(i2c_imx, 0);
|
||||
if (result) {
|
||||
/* timeout */
|
||||
if ((result == -ETIMEDOUT) && (i2c_imx->layerscape_bus_recover == 1))
|
||||
i2c_imx_recovery_for_layerscape(i2c_imx);
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Start I2C transfer */
|
||||
result = i2c_imx_start(i2c_imx);
|
||||
if (result) {
|
||||
|
@ -971,6 +1183,9 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
|||
pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
|
||||
|
||||
out:
|
||||
if (enable_runtime_pm)
|
||||
pm_runtime_disable(i2c_imx->adapter.dev.parent);
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
|
||||
(result < 0) ? "error" : "success msg",
|
||||
(result < 0) ? result : num);
|
||||
|
@ -1042,17 +1257,216 @@ static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* switch SCL and SDA to their GPIO function and do some bitbanging
|
||||
* for bus recovery.
|
||||
* There are platforms such as Layerscape that don't support pinctrl, so add
|
||||
* workaround for layerscape, it has no effect for other platforms.
|
||||
*/
|
||||
static int i2c_imx_init_recovery_for_layerscape(
|
||||
struct imx_i2c_struct *i2c_imx,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct pinmux_cfg *pinmux_cfg;
|
||||
struct device_node *scfg_node;
|
||||
void __iomem *scfg_base = NULL;
|
||||
|
||||
i2c_imx->gpio = of_get_named_gpio(np, "scl-gpios", 0);
|
||||
if (!gpio_is_valid(i2c_imx->gpio)) {
|
||||
dev_info(&pdev->dev, "scl-gpios not found\n");
|
||||
return 0;
|
||||
}
|
||||
pinmux_cfg = devm_kzalloc(&pdev->dev, sizeof(*pinmux_cfg), GFP_KERNEL);
|
||||
if (!pinmux_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_imx->need_set_pmuxcr = 0;
|
||||
of_id = of_match_node(pinmux_of_match, np);
|
||||
if (of_id) {
|
||||
pinmux_cfg = (struct pinmux_cfg *)of_id->data;
|
||||
i2c_imx->pmuxcr_endian = pinmux_cfg->endian;
|
||||
i2c_imx->pmuxcr_set = pinmux_cfg->pmuxcr_set_bit;
|
||||
scfg_node = of_find_matching_node(NULL, scfg_device_ids);
|
||||
if (scfg_node) {
|
||||
scfg_base = of_iomap(scfg_node, 0);
|
||||
if (scfg_base) {
|
||||
i2c_imx->pmuxcr_addr = scfg_base + pinmux_cfg->pmuxcr_offset;
|
||||
i2c_imx->need_set_pmuxcr = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
i2c_imx->layerscape_bus_recover = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 i2c_imx_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
static int i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
int temp;
|
||||
|
||||
/* Resume */
|
||||
temp = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
|
||||
if (temp < 0) {
|
||||
dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* Set slave addr. */
|
||||
imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
|
||||
|
||||
i2c_imx_reset_regs(i2c_imx);
|
||||
|
||||
/* Enable module */
|
||||
temp = i2c_imx->hwdata->i2cr_ien_opcode;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
/* Enable interrupt from i2c module */
|
||||
temp |= I2CR_IIEN;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
/* Wait controller to be stable */
|
||||
usleep_range(50, 150);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
unsigned int status, ctl;
|
||||
u8 value;
|
||||
|
||||
if (!i2c_imx->slave) {
|
||||
dev_err(&i2c_imx->adapter.dev, "cannot deal with slave irq,i2c_imx->slave is null");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
if (status & I2SR_IAL) { /* Arbitration lost */
|
||||
i2c_imx_clr_al_bit(status, i2c_imx);
|
||||
} else if (status & I2SR_IAAS) { /* Addressed as a slave */
|
||||
if (status & I2SR_SRW) { /* Master wants to read from us*/
|
||||
dev_dbg(&i2c_imx->adapter.dev, "read requested");
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
|
||||
|
||||
/* Slave transmit */
|
||||
ctl |= I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
/* Send data */
|
||||
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
|
||||
} else { /* Master wants to write to us */
|
||||
dev_dbg(&i2c_imx->adapter.dev, "write requested");
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
|
||||
|
||||
/* Slave receive */
|
||||
ctl &= ~I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
/* Dummy read */
|
||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
}
|
||||
} else if (!(ctl & I2CR_MTX)) { /* Receive mode */
|
||||
if (status & I2SR_IBB) { /* No STOP signal detected */
|
||||
ctl &= ~I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
} else { /* STOP signal is detected */
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"STOP signal detected");
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
|
||||
}
|
||||
} else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
|
||||
ctl |= I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
|
||||
|
||||
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
|
||||
} else { /* Transmit mode received NAK */
|
||||
ctl &= ~I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int i2c_imx_reg_slave(struct i2c_client *client)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
|
||||
int ret;
|
||||
if (i2c_imx->slave)
|
||||
return -EBUSY;
|
||||
|
||||
i2c_imx->slave = client;
|
||||
|
||||
ret = i2c_imx_slave_init(i2c_imx);
|
||||
if (ret < 0)
|
||||
dev_err(&i2c_imx->adapter.dev, "failed to switch to slave mode");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_imx_unreg_slave(struct i2c_client *client)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
|
||||
int ret;
|
||||
|
||||
if (!i2c_imx->slave)
|
||||
return -EINVAL;
|
||||
|
||||
/* Reset slave address. */
|
||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
|
||||
|
||||
i2c_imx_reset_regs(i2c_imx);
|
||||
|
||||
i2c_imx->slave = NULL;
|
||||
|
||||
/* Suspend */
|
||||
ret = pm_runtime_put_sync(i2c_imx->adapter.dev.parent);
|
||||
if (ret < 0)
|
||||
dev_err(&i2c_imx->adapter.dev, "failed to suspend i2c controller");
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct i2c_algorithm i2c_imx_algo = {
|
||||
.master_xfer = i2c_imx_xfer,
|
||||
.functionality = i2c_imx_func,
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
.reg_slave = i2c_imx_reg_slave,
|
||||
.unreg_slave = i2c_imx_unreg_slave,
|
||||
#endif
|
||||
};
|
||||
|
||||
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_id;
|
||||
unsigned int status;
|
||||
|
||||
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
|
||||
if (status & I2SR_IIF) {
|
||||
i2c_imx_clr_if_bit(status, i2c_imx);
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
if (i2c_imx->slave)
|
||||
return i2c_imx_slave_isr(i2c_imx);
|
||||
#endif
|
||||
return i2c_imx_master_isr(i2c_imx);
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int i2c_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx;
|
||||
|
@ -1113,7 +1527,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Request IRQ */
|
||||
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED,
|
||||
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND,
|
||||
pdev->name, i2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
|
@ -1146,15 +1561,28 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
i2c_imx->bitrate = pdata->bitrate;
|
||||
i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call;
|
||||
clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
|
||||
ret = i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't get I2C clock\n");
|
||||
goto clk_notifier_unregister;
|
||||
}
|
||||
|
||||
/* Set up chip registers to defaults */
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
||||
i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
|
||||
/*
|
||||
* This limit caused by an i.MX7D hardware issue(e7805 in Errata).
|
||||
* If there is no limit, when the bitrate set up to 400KHz, it will
|
||||
* cause the SCK low level period less than 1.3us.
|
||||
*/
|
||||
if (is_imx7d_i2c(i2c_imx) && i2c_imx->bitrate > IMX_I2C_MAX_E_BIT_RATE)
|
||||
i2c_imx->bitrate = IMX_I2C_MAX_E_BIT_RATE;
|
||||
|
||||
i2c_imx_reset_regs(i2c_imx);
|
||||
|
||||
/* Init optional bus recovery */
|
||||
if (of_match_node(pinmux_of_match, pdev->dev.of_node))
|
||||
ret = i2c_imx_init_recovery_for_layerscape(i2c_imx, pdev);
|
||||
else
|
||||
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
|
||||
|
||||
/* Init optional bus recovery function */
|
||||
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
|
||||
/* Give it another chance if pinctrl used is not ready yet */
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto clk_notifier_unregister;
|
||||
|
@ -1226,7 +1654,8 @@ static int __maybe_unused i2c_imx_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(i2c_imx->clk);
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1236,14 +1665,28 @@ static int __maybe_unused i2c_imx_runtime_resume(struct device *dev)
|
|||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(i2c_imx->clk);
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
ret = clk_prepare_enable(i2c_imx->clk);
|
||||
if (ret)
|
||||
dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_imx_suspend(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_resume(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops i2c_imx_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
|
||||
SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
|
||||
i2c_imx_runtime_resume, NULL)
|
||||
};
|
||||
|
|
462
drivers/i2c/busses/i2c-rpmsg-imx.c
Normal file
462
drivers/i2c/busses/i2c-rpmsg-imx.c
Normal file
|
@ -0,0 +1,462 @@
|
|||
/*
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
/* The i2c-rpmsg transfer protocol:
|
||||
*
|
||||
* +---------------+-------------------------------+
|
||||
* | Byte Offset | Content |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 0 | Category |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 1 ~ 2 | Version |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 3 | Type |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 4 | Command |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 5 | Priority |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 6 | Reserved1 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 7 | Reserved2 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 8 | Reserved3 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 9 | Reserved4 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 10 | BUS ID |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 11 | Return Value |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 12 ~ 13 | BUS ID |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 14 ~ 15 | Address |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 16 ~ 17 | Data Len |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 18 ~ 33 | 16 Bytes Data |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
*
|
||||
* The definition of Return Value:
|
||||
* 0x00 = Success
|
||||
* 0x01 = Failed
|
||||
* 0x02 = Invalid parameter
|
||||
* 0x03 = Invalid message
|
||||
* 0x04 = Operate in invalid state
|
||||
* 0x05 = Memory allocation failed
|
||||
* 0x06 = Timeout when waiting for an event
|
||||
* 0x07 = Cannot add to list as node already in another list
|
||||
* 0x08 = Cannot remove from list as node not in list
|
||||
* 0x09 = Transfer timeout
|
||||
* 0x0A = Transfer failed due to peer core not ready
|
||||
* 0x0B = Transfer failed due to communication failure
|
||||
* 0x0C = Cannot find service for a request/notification
|
||||
* 0x0D = Service version cannot support the request/notification
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/imx_rpmsg.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/rpmsg.h>
|
||||
|
||||
#define I2C_RPMSG_MAX_BUF_SIZE 16
|
||||
#define I2C_RPMSG_TIMEOUT 500 /* unit: ms */
|
||||
|
||||
#define I2C_RPMSG_CATEGORY 0x09
|
||||
#define I2C_RPMSG_VERSION 0x0001
|
||||
#define I2C_RPMSG_TYPE_REQUEST 0x00
|
||||
#define I2C_RPMSG_TYPE_RESPONSE 0x01
|
||||
#define I2C_RPMSG_COMMAND_READ 0x00
|
||||
#define I2C_RPMSG_COMMAND_WRITE 0x01
|
||||
#define I2C_RPMSG_PRIORITY 0x01
|
||||
|
||||
#define I2C_RPMSG_M_STOP 0x0200
|
||||
|
||||
struct i2c_rpmsg_msg {
|
||||
struct imx_rpmsg_head header;
|
||||
|
||||
/* Payload Start*/
|
||||
u8 bus_id;
|
||||
u8 ret_val;
|
||||
u16 addr;
|
||||
u16 flags;
|
||||
u16 len;
|
||||
u8 buf[I2C_RPMSG_MAX_BUF_SIZE];
|
||||
} __packed __aligned(1);
|
||||
|
||||
struct i2c_rpmsg_info {
|
||||
struct rpmsg_device *rpdev;
|
||||
struct device *dev;
|
||||
struct i2c_rpmsg_msg *msg;
|
||||
struct completion cmd_complete;
|
||||
struct mutex lock;
|
||||
|
||||
u8 bus_id;
|
||||
u16 addr;
|
||||
};
|
||||
|
||||
static struct i2c_rpmsg_info i2c_rpmsg;
|
||||
|
||||
struct imx_rpmsg_i2c_data {
|
||||
struct i2c_adapter adapter;
|
||||
};
|
||||
|
||||
static int i2c_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
|
||||
void *priv, u32 src)
|
||||
{
|
||||
struct i2c_rpmsg_msg *msg = (struct i2c_rpmsg_msg *)data;
|
||||
|
||||
if (msg->header.type != I2C_RPMSG_TYPE_RESPONSE)
|
||||
return -EINVAL;
|
||||
|
||||
if (msg->bus_id != i2c_rpmsg.bus_id || msg->addr != i2c_rpmsg.addr) {
|
||||
dev_err(&rpdev->dev,
|
||||
"expected bus_id:%d, addr:%2x, received bus_id:%d, addr:%2x\n",
|
||||
i2c_rpmsg.bus_id, i2c_rpmsg.addr, msg->bus_id, msg->addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) {
|
||||
dev_err(&rpdev->dev,
|
||||
"%s failed: data length greater than %d, len=%d\n",
|
||||
__func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Receive Success */
|
||||
i2c_rpmsg.msg = msg;
|
||||
|
||||
complete(&i2c_rpmsg.cmd_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpmsg_xfer(struct i2c_rpmsg_msg *rmsg, struct i2c_rpmsg_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = rpmsg_send(info->rpdev->ept, (void *)rmsg,
|
||||
sizeof(struct i2c_rpmsg_msg));
|
||||
if (ret < 0) {
|
||||
dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wait_for_completion_timeout(&info->cmd_complete,
|
||||
msecs_to_jiffies(I2C_RPMSG_TIMEOUT));
|
||||
if (!ret) {
|
||||
dev_err(&info->rpdev->dev, "%s failed: timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (info->msg->ret_val) {
|
||||
dev_dbg(&info->rpdev->dev,
|
||||
"%s failed: %d\n", __func__, info->msg->ret_val);
|
||||
return -(info->msg->ret_val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_rpmsg_read(struct i2c_msg *msg, struct i2c_rpmsg_info *info,
|
||||
int bus_id, bool is_last)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_rpmsg_msg rmsg;
|
||||
|
||||
if (!info->rpdev)
|
||||
return -EINVAL;
|
||||
|
||||
if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) {
|
||||
dev_err(&info->rpdev->dev,
|
||||
"%s failed: data length greater than %d, len=%d\n",
|
||||
__func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&rmsg, 0, sizeof(struct i2c_rpmsg_msg));
|
||||
rmsg.header.cate = I2C_RPMSG_CATEGORY;
|
||||
rmsg.header.major = I2C_RPMSG_VERSION;
|
||||
rmsg.header.minor = I2C_RPMSG_VERSION >> 8;
|
||||
rmsg.header.type = I2C_RPMSG_TYPE_REQUEST;
|
||||
rmsg.header.cmd = I2C_RPMSG_COMMAND_READ;
|
||||
rmsg.header.reserved[0] = I2C_RPMSG_PRIORITY;
|
||||
rmsg.bus_id = bus_id;
|
||||
rmsg.ret_val = 0;
|
||||
rmsg.addr = msg->addr;
|
||||
if (is_last)
|
||||
rmsg.flags = msg->flags | I2C_RPMSG_M_STOP;
|
||||
else
|
||||
rmsg.flags = msg->flags;
|
||||
rmsg.len = (msg->len);
|
||||
|
||||
reinit_completion(&info->cmd_complete);
|
||||
|
||||
ret = rpmsg_xfer(&rmsg, info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!info->msg ||
|
||||
(info->msg->len != msg->len)) {
|
||||
dev_err(&info->rpdev->dev,
|
||||
"%s failed: %d\n", __func__, -EPROTO);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
memcpy(msg->buf, info->msg->buf, info->msg->len);
|
||||
|
||||
return msg->len;
|
||||
}
|
||||
|
||||
int i2c_rpmsg_write(struct i2c_msg *msg, struct i2c_rpmsg_info *info,
|
||||
int bus_id, bool is_last)
|
||||
{
|
||||
int i, ret;
|
||||
struct i2c_rpmsg_msg rmsg;
|
||||
|
||||
if (!info || !info->rpdev)
|
||||
return -EINVAL;
|
||||
|
||||
if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) {
|
||||
dev_err(&info->rpdev->dev,
|
||||
"%s failed: data length greater than %d, len=%d\n",
|
||||
__func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&rmsg, 0, sizeof(struct i2c_rpmsg_msg));
|
||||
rmsg.header.cate = I2C_RPMSG_CATEGORY;
|
||||
rmsg.header.major = I2C_RPMSG_VERSION;
|
||||
rmsg.header.minor = I2C_RPMSG_VERSION >> 8;
|
||||
rmsg.header.type = I2C_RPMSG_TYPE_REQUEST;
|
||||
rmsg.header.cmd = I2C_RPMSG_COMMAND_WRITE;
|
||||
rmsg.header.reserved[0] = I2C_RPMSG_PRIORITY;
|
||||
rmsg.bus_id = bus_id;
|
||||
rmsg.ret_val = 0;
|
||||
rmsg.addr = msg->addr;
|
||||
if (is_last)
|
||||
rmsg.flags = msg->flags | I2C_RPMSG_M_STOP;
|
||||
else
|
||||
rmsg.flags = msg->flags;
|
||||
rmsg.len = msg->len;
|
||||
|
||||
for (i = 0; i < rmsg.len; i++)
|
||||
rmsg.buf[i] = msg->buf[i];
|
||||
|
||||
reinit_completion(&info->cmd_complete);
|
||||
|
||||
ret = rpmsg_xfer(&rmsg, info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!rpdev) {
|
||||
dev_info(&rpdev->dev, "%s failed, rpdev=NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2c_rpmsg.rpdev = rpdev;
|
||||
|
||||
mutex_init(&i2c_rpmsg.lock);
|
||||
init_completion(&i2c_rpmsg.cmd_complete);
|
||||
|
||||
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
|
||||
rpdev->src, rpdev->dst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void i2c_rpmsg_remove(struct rpmsg_device *rpdev)
|
||||
{
|
||||
i2c_rpmsg.rpdev = NULL;
|
||||
dev_info(&rpdev->dev, "i2c rpmsg driver is removed\n");
|
||||
}
|
||||
|
||||
static struct rpmsg_device_id i2c_rpmsg_id_table[] = {
|
||||
{ .name = "rpmsg-i2c-channel" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct rpmsg_driver i2c_rpmsg_driver = {
|
||||
.drv.name = "i2c-rpmsg",
|
||||
.drv.owner = THIS_MODULE,
|
||||
.id_table = i2c_rpmsg_id_table,
|
||||
.probe = i2c_rpmsg_probe,
|
||||
.remove = i2c_rpmsg_remove,
|
||||
.callback = i2c_rpmsg_cb,
|
||||
};
|
||||
|
||||
|
||||
static int i2c_rpbus_xfer(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct imx_rpmsg_i2c_data *rdata =
|
||||
container_of(adapter, struct imx_rpmsg_i2c_data, adapter);
|
||||
struct i2c_msg *pmsg;
|
||||
int i, ret;
|
||||
bool is_last = false;
|
||||
|
||||
mutex_lock(&i2c_rpmsg.lock);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (i == num - 1)
|
||||
is_last = true;
|
||||
|
||||
pmsg = &msgs[i];
|
||||
|
||||
i2c_rpmsg.bus_id = rdata->adapter.nr;
|
||||
i2c_rpmsg.addr = pmsg->addr;
|
||||
|
||||
if (pmsg->flags & I2C_M_RD) {
|
||||
ret = i2c_rpmsg_read(pmsg, &i2c_rpmsg,
|
||||
rdata->adapter.nr, is_last);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&i2c_rpmsg.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmsg->len = ret;
|
||||
} else {
|
||||
ret = i2c_rpmsg_write(pmsg, &i2c_rpmsg,
|
||||
rdata->adapter.nr, is_last);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&i2c_rpmsg.lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&i2c_rpmsg.lock);
|
||||
return num;
|
||||
}
|
||||
|
||||
static u32 i2c_rpbus_func(struct i2c_adapter *a)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm i2c_rpbus_algorithm = {
|
||||
.master_xfer = i2c_rpbus_xfer,
|
||||
.functionality = i2c_rpbus_func,
|
||||
};
|
||||
|
||||
static const struct i2c_adapter_quirks i2c_rpbus_quirks = {
|
||||
.max_write_len = I2C_RPMSG_MAX_BUF_SIZE,
|
||||
.max_read_len = I2C_RPMSG_MAX_BUF_SIZE,
|
||||
};
|
||||
|
||||
static int i2c_rpbus_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct imx_rpmsg_i2c_data *rdata;
|
||||
struct i2c_adapter *adapter;
|
||||
int ret;
|
||||
|
||||
if (!i2c_rpmsg.rpdev)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata), GFP_KERNEL);
|
||||
if (!rdata)
|
||||
return -ENOMEM;
|
||||
|
||||
adapter = &rdata->adapter;
|
||||
/* setup i2c adapter description */
|
||||
adapter->owner = THIS_MODULE;
|
||||
adapter->class = I2C_CLASS_HWMON;
|
||||
adapter->algo = &i2c_rpbus_algorithm;
|
||||
adapter->dev.parent = dev;
|
||||
adapter->dev.of_node = np;
|
||||
adapter->nr = of_alias_get_id(np, "i2c");
|
||||
/*
|
||||
* The driver will send the adapter->nr as BUS ID to the other
|
||||
* side, and the other side will check the BUS ID to see whether
|
||||
* the BUS has been registered. If there is alias id for this
|
||||
* virtual adapter, linux kernel will automatically allocate one
|
||||
* id which might be not the same number used in the other side,
|
||||
* cause i2c slave probe failure under this virtual I2C bus.
|
||||
* So let's add a BUG_ON to catch this issue earlier.
|
||||
*/
|
||||
BUG_ON(adapter->nr < 0);
|
||||
adapter->quirks = &i2c_rpbus_quirks;
|
||||
snprintf(rdata->adapter.name, sizeof(rdata->adapter.name), "%s",
|
||||
"i2c-rpmsg-adapter");
|
||||
platform_set_drvdata(pdev, rdata);
|
||||
|
||||
ret = i2c_add_adapter(&rdata->adapter);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to add I2C adapter: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "add I2C adapter %s successfully\n", rdata->adapter.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_rpbus_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_rpmsg_i2c_data *rdata = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&rdata->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_rpmsg_i2c_dt_ids[] = {
|
||||
{ .compatible = "fsl,i2c-rpbus", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_rpmsg_i2c_dt_ids);
|
||||
|
||||
static struct platform_driver imx_rpmsg_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "imx_rpmsg_i2c",
|
||||
.of_match_table = imx_rpmsg_i2c_dt_ids,
|
||||
},
|
||||
.probe = i2c_rpbus_probe,
|
||||
.remove = i2c_rpbus_remove
|
||||
};
|
||||
|
||||
static int __init imx_rpmsg_i2c_driver_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = register_rpmsg_driver(&i2c_rpmsg_driver);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return platform_driver_register(&(imx_rpmsg_i2c_driver));
|
||||
}
|
||||
subsys_initcall(imx_rpmsg_i2c_driver_init);
|
||||
|
||||
MODULE_AUTHOR("Clark Wang<xiaoning.wang@nxp.com>");
|
||||
MODULE_DESCRIPTION("Driver for i2c over rpmsg");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:i2c-rpbus");
|
|
@ -64,6 +64,7 @@ enum pca_type {
|
|||
pca_9546,
|
||||
pca_9547,
|
||||
pca_9548,
|
||||
pca_9646,
|
||||
pca_9846,
|
||||
pca_9847,
|
||||
pca_9848,
|
||||
|
@ -86,7 +87,7 @@ struct pca954x {
|
|||
|
||||
u8 last_chan; /* last register value */
|
||||
/* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
|
||||
s8 idle_state;
|
||||
s32 idle_state;
|
||||
|
||||
struct i2c_client *client;
|
||||
|
||||
|
@ -145,6 +146,11 @@ static const struct chip_desc chips[] = {
|
|||
.muxtype = pca954x_isswi,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9646] = {
|
||||
.nchans = 4,
|
||||
.muxtype = pca954x_isswi,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9846] = {
|
||||
.nchans = 4,
|
||||
.muxtype = pca954x_isswi,
|
||||
|
@ -190,6 +196,7 @@ static const struct i2c_device_id pca954x_id[] = {
|
|||
{ "pca9546", pca_9546 },
|
||||
{ "pca9547", pca_9547 },
|
||||
{ "pca9548", pca_9548 },
|
||||
{ "pca9646", pca_9646 },
|
||||
{ "pca9846", pca_9846 },
|
||||
{ "pca9847", pca_9847 },
|
||||
{ "pca9848", pca_9848 },
|
||||
|
@ -208,6 +215,7 @@ static const struct of_device_id pca954x_of_match[] = {
|
|||
{ .compatible = "nxp,pca9546", .data = &chips[pca_9546] },
|
||||
{ .compatible = "nxp,pca9547", .data = &chips[pca_9547] },
|
||||
{ .compatible = "nxp,pca9548", .data = &chips[pca_9548] },
|
||||
{ .compatible = "nxp,pca9646", .data = &chips[pca_9646] },
|
||||
{ .compatible = "nxp,pca9846", .data = &chips[pca_9846] },
|
||||
{ .compatible = "nxp,pca9847", .data = &chips[pca_9847] },
|
||||
{ .compatible = "nxp,pca9848", .data = &chips[pca_9848] },
|
||||
|
@ -229,20 +237,23 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
|
|||
I2C_SMBUS_BYTE, &dummy);
|
||||
}
|
||||
|
||||
static u8 pca954x_regval(struct pca954x *data, u8 chan)
|
||||
{
|
||||
/* We make switches look like muxes, not sure how to be smarter. */
|
||||
if (data->chip->muxtype == pca954x_ismux)
|
||||
return chan | data->chip->enable;
|
||||
else
|
||||
return 1 << chan;
|
||||
}
|
||||
|
||||
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct pca954x *data = i2c_mux_priv(muxc);
|
||||
struct i2c_client *client = data->client;
|
||||
const struct chip_desc *chip = data->chip;
|
||||
u8 regval;
|
||||
int ret = 0;
|
||||
|
||||
/* we make switches look like muxes, not sure how to be smarter */
|
||||
if (chip->muxtype == pca954x_ismux)
|
||||
regval = chan | chip->enable;
|
||||
else
|
||||
regval = 1 << chan;
|
||||
|
||||
regval = pca954x_regval(data, chan);
|
||||
/* Only select the channel if its different from the last channel */
|
||||
if (data->last_chan != regval) {
|
||||
ret = pca954x_reg_write(muxc->parent, client, regval);
|
||||
|
@ -256,7 +267,7 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
|
|||
{
|
||||
struct pca954x *data = i2c_mux_priv(muxc);
|
||||
struct i2c_client *client = data->client;
|
||||
s8 idle_state;
|
||||
s32 idle_state;
|
||||
|
||||
idle_state = READ_ONCE(data->idle_state);
|
||||
if (idle_state >= 0)
|
||||
|
@ -402,6 +413,22 @@ static void pca954x_cleanup(struct i2c_mux_core *muxc)
|
|||
i2c_mux_del_adapters(muxc);
|
||||
}
|
||||
|
||||
static int pca954x_init(struct i2c_client *client, struct pca954x *data)
|
||||
{
|
||||
int ret;
|
||||
if (data->idle_state >= 0) {
|
||||
data->last_chan = pca954x_regval(data, data->idle_state);
|
||||
} else {
|
||||
/* Disconnect multiplexer */
|
||||
data->last_chan = 0;
|
||||
}
|
||||
ret = i2c_smbus_write_byte(client, data->last_chan);
|
||||
if (ret < 0)
|
||||
data->last_chan = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C init/probing/exit functions
|
||||
*/
|
||||
|
@ -411,7 +438,6 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
struct i2c_adapter *adap = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
bool idle_disconnect_dt;
|
||||
struct gpio_desc *gpio;
|
||||
struct i2c_mux_core *muxc;
|
||||
struct pca954x *data;
|
||||
|
@ -462,23 +488,24 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
}
|
||||
}
|
||||
|
||||
/* Write the mux register at addr to verify
|
||||
data->idle_state = MUX_IDLE_AS_IS;
|
||||
if (of_property_read_u32(np, "idle-state", &data->idle_state)) {
|
||||
if (np && of_property_read_bool(np, "i2c-mux-idle-disconnect"))
|
||||
data->idle_state = MUX_IDLE_DISCONNECT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the mux register at addr to verify
|
||||
* that the mux is in fact present. This also
|
||||
* initializes the mux to disconnected state.
|
||||
* initializes the mux to a channel
|
||||
* or disconnected state.
|
||||
*/
|
||||
if (i2c_smbus_write_byte(client, 0) < 0) {
|
||||
ret = pca954x_init(client, data);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev, "probe failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->last_chan = 0; /* force the first selection */
|
||||
data->idle_state = MUX_IDLE_AS_IS;
|
||||
|
||||
idle_disconnect_dt = np &&
|
||||
of_property_read_bool(np, "i2c-mux-idle-disconnect");
|
||||
if (idle_disconnect_dt)
|
||||
data->idle_state = MUX_IDLE_DISCONNECT;
|
||||
|
||||
ret = pca954x_irq_setup(muxc);
|
||||
if (ret)
|
||||
goto fail_cleanup;
|
||||
|
@ -530,9 +557,13 @@ static int pca954x_resume(struct device *dev)
|
|||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||
struct pca954x *data = i2c_mux_priv(muxc);
|
||||
int ret;
|
||||
|
||||
data->last_chan = 0;
|
||||
return i2c_smbus_write_byte(client, 0);
|
||||
ret = pca954x_init(client, data);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "failed to verify the mux, the mux maybe not present in fact\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user