1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Wondermedia I2C Controller Driver
5 * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
7 * Derived from GPLv2+ licensed source:
8 * - Copyright (C) 2008 WonderMedia Technologies, Inc.
11 #include <linux/clk.h>
13 #include <linux/of_address.h>
14 #include "i2c-viai2c-common.h"
16 #define REG_SLAVE_CR 0x10
17 #define REG_SLAVE_SR 0x12
18 #define REG_SLAVE_ISR 0x14
19 #define REG_SLAVE_IMR 0x16
20 #define REG_SLAVE_DR 0x18
21 #define REG_SLAVE_TR 0x1A
24 #define SCL_TIMEOUT(x) (((x) & 0xFF) << 8)
30 #define MCR_APB_166M 12
32 static u32
wmt_i2c_func(struct i2c_adapter
*adap
)
34 return I2C_FUNC_I2C
| I2C_FUNC_SMBUS_EMUL
| I2C_FUNC_NOSTART
;
37 static const struct i2c_algorithm wmt_i2c_algo
= {
39 .functionality
= wmt_i2c_func
,
42 static int wmt_i2c_reset_hardware(struct viai2c
*i2c
)
46 err
= clk_prepare_enable(i2c
->clk
);
48 dev_err(i2c
->dev
, "failed to enable clock\n");
52 err
= clk_set_rate(i2c
->clk
, 20000000);
54 dev_err(i2c
->dev
, "failed to set clock = 20Mhz\n");
55 clk_disable_unprepare(i2c
->clk
);
59 writew(0, i2c
->base
+ VIAI2C_REG_CR
);
60 writew(MCR_APB_166M
, i2c
->base
+ VIAI2C_REG_MCR
);
61 writew(VIAI2C_ISR_MASK_ALL
, i2c
->base
+ VIAI2C_REG_ISR
);
62 writew(VIAI2C_IMR_ENABLE_ALL
, i2c
->base
+ VIAI2C_REG_IMR
);
63 writew(VIAI2C_CR_ENABLE
, i2c
->base
+ VIAI2C_REG_CR
);
64 readw(i2c
->base
+ VIAI2C_REG_CSR
); /* read clear */
65 writew(VIAI2C_ISR_MASK_ALL
, i2c
->base
+ VIAI2C_REG_ISR
);
67 if (i2c
->tcr
== VIAI2C_TCR_FAST
)
68 writew(SCL_TIMEOUT(128) | TR_HS
, i2c
->base
+ VIAI2C_REG_TR
);
70 writew(SCL_TIMEOUT(128) | TR_STD
, i2c
->base
+ VIAI2C_REG_TR
);
75 static irqreturn_t
wmt_i2c_isr(int irq
, void *data
)
77 struct viai2c
*i2c
= data
;
80 /* save the status and write-clear it */
81 status
= readw(i2c
->base
+ VIAI2C_REG_ISR
);
82 writew(status
, i2c
->base
+ VIAI2C_REG_ISR
);
85 if (status
& VIAI2C_ISR_NACK_ADDR
)
88 if (status
& VIAI2C_ISR_SCL_TIMEOUT
)
89 i2c
->ret
= -ETIMEDOUT
;
92 i2c
->ret
= viai2c_irq_xfer(i2c
);
94 /* All the data has been successfully transferred or error occurred */
96 complete(&i2c
->complete
);
101 static int wmt_i2c_probe(struct platform_device
*pdev
)
103 struct device_node
*np
= pdev
->dev
.of_node
;
105 struct i2c_adapter
*adap
;
109 err
= viai2c_init(pdev
, &i2c
, VIAI2C_PLAT_WMT
);
113 i2c
->irq
= platform_get_irq(pdev
, 0);
117 err
= devm_request_irq(&pdev
->dev
, i2c
->irq
, wmt_i2c_isr
,
120 return dev_err_probe(&pdev
->dev
, err
,
121 "failed to request irq %i\n", i2c
->irq
);
123 i2c
->clk
= of_clk_get(np
, 0);
124 if (IS_ERR(i2c
->clk
)) {
125 dev_err(&pdev
->dev
, "unable to request clock\n");
126 return PTR_ERR(i2c
->clk
);
129 err
= of_property_read_u32(np
, "clock-frequency", &clk_rate
);
130 if (!err
&& clk_rate
== I2C_MAX_FAST_MODE_FREQ
)
131 i2c
->tcr
= VIAI2C_TCR_FAST
;
133 adap
= &i2c
->adapter
;
134 i2c_set_adapdata(adap
, i2c
);
135 strscpy(adap
->name
, "WMT I2C adapter", sizeof(adap
->name
));
136 adap
->owner
= THIS_MODULE
;
137 adap
->algo
= &wmt_i2c_algo
;
138 adap
->dev
.parent
= &pdev
->dev
;
139 adap
->dev
.of_node
= pdev
->dev
.of_node
;
141 err
= wmt_i2c_reset_hardware(i2c
);
143 dev_err(&pdev
->dev
, "error initializing hardware\n");
147 err
= i2c_add_adapter(adap
);
149 /* wmt_i2c_reset_hardware() enables i2c_dev->clk */
150 clk_disable_unprepare(i2c
->clk
);
155 static void wmt_i2c_remove(struct platform_device
*pdev
)
157 struct viai2c
*i2c
= platform_get_drvdata(pdev
);
159 /* Disable interrupts, clock and delete adapter */
160 writew(0, i2c
->base
+ VIAI2C_REG_IMR
);
161 clk_disable_unprepare(i2c
->clk
);
162 i2c_del_adapter(&i2c
->adapter
);
165 static const struct of_device_id wmt_i2c_dt_ids
[] = {
166 { .compatible
= "wm,wm8505-i2c" },
170 static struct platform_driver wmt_i2c_driver
= {
171 .probe
= wmt_i2c_probe
,
172 .remove_new
= wmt_i2c_remove
,
175 .of_match_table
= wmt_i2c_dt_ids
,
179 module_platform_driver(wmt_i2c_driver
);
181 MODULE_DESCRIPTION("Wondermedia I2C controller driver");
182 MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
183 MODULE_LICENSE("GPL");
184 MODULE_DEVICE_TABLE(of
, wmt_i2c_dt_ids
);