1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *  Wondermedia I2C Master Mode Driver
4 *
5 *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
6 *
7 *  Derived from GPLv2+ licensed source:
8 *  - Copyright (C) 2008 WonderMedia Technologies, Inc.
9 */
10
11#include <linux/clk.h>
12#include <linux/of.h>
13#include <linux/of_address.h>
14#include "i2c-viai2c-common.h"
15
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
22
23/* REG_TR */
24#define SCL_TIMEOUT(x)		(((x) & 0xFF) << 8)
25#define TR_STD			0x0064
26#define TR_HS			0x0019
27
28/* REG_MCR */
29#define MCR_APB_96M		7
30#define MCR_APB_166M		12
31
32static u32 wmt_i2c_func(struct i2c_adapter *adap)
33{
34	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART;
35}
36
37static const struct i2c_algorithm wmt_i2c_algo = {
38	.master_xfer	= viai2c_xfer,
39	.functionality	= wmt_i2c_func,
40};
41
42static int wmt_i2c_reset_hardware(struct viai2c *i2c)
43{
44	int err;
45
46	err = clk_prepare_enable(i2c->clk);
47	if (err) {
48		dev_err(i2c->dev, "failed to enable clock\n");
49		return err;
50	}
51
52	err = clk_set_rate(i2c->clk, 20000000);
53	if (err) {
54		dev_err(i2c->dev, "failed to set clock = 20Mhz\n");
55		clk_disable_unprepare(i2c->clk);
56		return err;
57	}
58
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);
66
67	if (i2c->tcr == VIAI2C_TCR_FAST)
68		writew(SCL_TIMEOUT(128) | TR_HS, i2c->base + VIAI2C_REG_TR);
69	else
70		writew(SCL_TIMEOUT(128) | TR_STD, i2c->base + VIAI2C_REG_TR);
71
72	return 0;
73}
74
75static int wmt_i2c_probe(struct platform_device *pdev)
76{
77	struct device_node *np = pdev->dev.of_node;
78	struct viai2c *i2c;
79	struct i2c_adapter *adap;
80	int err;
81	u32 clk_rate;
82
83	err = viai2c_init(pdev, &i2c, VIAI2C_PLAT_WMT);
84	if (err)
85		return err;
86
87	i2c->clk = of_clk_get(np, 0);
88	if (IS_ERR(i2c->clk)) {
89		dev_err(&pdev->dev, "unable to request clock\n");
90		return PTR_ERR(i2c->clk);
91	}
92
93	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
94	if (!err && clk_rate == I2C_MAX_FAST_MODE_FREQ)
95		i2c->tcr = VIAI2C_TCR_FAST;
96
97	adap = &i2c->adapter;
98	i2c_set_adapdata(adap, i2c);
99	strscpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
100	adap->owner = THIS_MODULE;
101	adap->algo = &wmt_i2c_algo;
102	adap->dev.parent = &pdev->dev;
103	adap->dev.of_node = pdev->dev.of_node;
104
105	err = wmt_i2c_reset_hardware(i2c);
106	if (err) {
107		dev_err(&pdev->dev, "error initializing hardware\n");
108		return err;
109	}
110
111	err = i2c_add_adapter(adap);
112	if (err)
113		/* wmt_i2c_reset_hardware() enables i2c_dev->clk */
114		clk_disable_unprepare(i2c->clk);
115
116	return err;
117}
118
119static void wmt_i2c_remove(struct platform_device *pdev)
120{
121	struct viai2c *i2c = platform_get_drvdata(pdev);
122
123	/* Disable interrupts, clock and delete adapter */
124	writew(0, i2c->base + VIAI2C_REG_IMR);
125	clk_disable_unprepare(i2c->clk);
126	i2c_del_adapter(&i2c->adapter);
127}
128
129static const struct of_device_id wmt_i2c_dt_ids[] = {
130	{ .compatible = "wm,wm8505-i2c" },
131	{ /* Sentinel */ },
132};
133
134static struct platform_driver wmt_i2c_driver = {
135	.probe		= wmt_i2c_probe,
136	.remove_new	= wmt_i2c_remove,
137	.driver		= {
138		.name	= "wmt-i2c",
139		.of_match_table = wmt_i2c_dt_ids,
140	},
141};
142
143module_platform_driver(wmt_i2c_driver);
144
145MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
146MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
147MODULE_LICENSE("GPL");
148MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
149