1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2021 Linaro Ltd.
4  * Author: Sam Protsenko <semen.protsenko@linaro.org>
5  *
6  * Samsung Exynos USI driver (Universal Serial Interface).
7  */
8 
9 #include <linux/array_size.h>
10 #include <linux/clk.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_platform.h>
15 #include <linux/platform_device.h>
16 #include <linux/regmap.h>
17 
18 #include <dt-bindings/soc/samsung,exynos-usi.h>
19 
20 /* USIv2: System Register: SW_CONF register bits */
21 #define USI_V2_SW_CONF_NONE	0x0
22 #define USI_V2_SW_CONF_UART	BIT(0)
23 #define USI_V2_SW_CONF_SPI	BIT(1)
24 #define USI_V2_SW_CONF_I2C	BIT(2)
25 #define USI_V2_SW_CONF_MASK	(USI_V2_SW_CONF_UART | USI_V2_SW_CONF_SPI | \
26 				 USI_V2_SW_CONF_I2C)
27 
28 /* USIv2: USI register offsets */
29 #define USI_CON			0x04
30 #define USI_OPTION		0x08
31 
32 /* USIv2: USI register bits */
33 #define USI_CON_RESET		BIT(0)
34 #define USI_OPTION_CLKREQ_ON	BIT(1)
35 #define USI_OPTION_CLKSTOP_ON	BIT(2)
36 
37 enum exynos_usi_ver {
38 	USI_VER2 = 2,
39 };
40 
41 struct exynos_usi_variant {
42 	enum exynos_usi_ver ver;	/* USI IP-core version */
43 	unsigned int sw_conf_mask;	/* SW_CONF mask for all protocols */
44 	size_t min_mode;		/* first index in exynos_usi_modes[] */
45 	size_t max_mode;		/* last index in exynos_usi_modes[] */
46 	size_t num_clks;		/* number of clocks to assert */
47 	const char * const *clk_names;	/* clock names to assert */
48 };
49 
50 struct exynos_usi {
51 	struct device *dev;
52 	void __iomem *regs;		/* USI register map */
53 	struct clk_bulk_data *clks;	/* USI clocks */
54 
55 	size_t mode;			/* current USI SW_CONF mode index */
56 	bool clkreq_on;			/* always provide clock to IP */
57 
58 	/* System Register */
59 	struct regmap *sysreg;		/* System Register map */
60 	unsigned int sw_conf;		/* SW_CONF register offset in sysreg */
61 
62 	const struct exynos_usi_variant *data;
63 };
64 
65 struct exynos_usi_mode {
66 	const char *name;		/* mode name */
67 	unsigned int val;		/* mode register value */
68 };
69 
70 static const struct exynos_usi_mode exynos_usi_modes[] = {
71 	[USI_V2_NONE] =	{ .name = "none", .val = USI_V2_SW_CONF_NONE },
72 	[USI_V2_UART] =	{ .name = "uart", .val = USI_V2_SW_CONF_UART },
73 	[USI_V2_SPI] =	{ .name = "spi",  .val = USI_V2_SW_CONF_SPI },
74 	[USI_V2_I2C] =	{ .name = "i2c",  .val = USI_V2_SW_CONF_I2C },
75 };
76 
77 static const char * const exynos850_usi_clk_names[] = { "pclk", "ipclk" };
78 static const struct exynos_usi_variant exynos850_usi_data = {
79 	.ver		= USI_VER2,
80 	.sw_conf_mask	= USI_V2_SW_CONF_MASK,
81 	.min_mode	= USI_V2_NONE,
82 	.max_mode	= USI_V2_I2C,
83 	.num_clks	= ARRAY_SIZE(exynos850_usi_clk_names),
84 	.clk_names	= exynos850_usi_clk_names,
85 };
86 
87 static const struct of_device_id exynos_usi_dt_match[] = {
88 	{
89 		.compatible = "samsung,exynos850-usi",
90 		.data = &exynos850_usi_data,
91 	},
92 	{ } /* sentinel */
93 };
94 MODULE_DEVICE_TABLE(of, exynos_usi_dt_match);
95 
96 /**
97  * exynos_usi_set_sw_conf - Set USI block configuration mode
98  * @usi: USI driver object
99  * @mode: Mode index
100  *
101  * Select underlying serial protocol (UART/SPI/I2C) in USI IP-core.
102  *
103  * Return: 0 on success, or negative error code on failure.
104  */
exynos_usi_set_sw_conf(struct exynos_usi * usi,size_t mode)105 static int exynos_usi_set_sw_conf(struct exynos_usi *usi, size_t mode)
106 {
107 	unsigned int val;
108 	int ret;
109 
110 	if (mode < usi->data->min_mode || mode > usi->data->max_mode)
111 		return -EINVAL;
112 
113 	val = exynos_usi_modes[mode].val;
114 	ret = regmap_update_bits(usi->sysreg, usi->sw_conf,
115 				 usi->data->sw_conf_mask, val);
116 	if (ret)
117 		return ret;
118 
119 	usi->mode = mode;
120 	dev_dbg(usi->dev, "protocol: %s\n", exynos_usi_modes[usi->mode].name);
121 
122 	return 0;
123 }
124 
125 /**
126  * exynos_usi_enable - Initialize USI block
127  * @usi: USI driver object
128  *
129  * USI IP-core start state is "reset" (on startup and after CPU resume). This
130  * routine enables the USI block by clearing the reset flag. It also configures
131  * HWACG behavior (needed e.g. for UART Rx). It should be performed before
132  * underlying protocol becomes functional.
133  *
134  * Return: 0 on success, or negative error code on failure.
135  */
exynos_usi_enable(const struct exynos_usi * usi)136 static int exynos_usi_enable(const struct exynos_usi *usi)
137 {
138 	u32 val;
139 	int ret;
140 
141 	ret = clk_bulk_prepare_enable(usi->data->num_clks, usi->clks);
142 	if (ret)
143 		return ret;
144 
145 	/* Enable USI block */
146 	val = readl(usi->regs + USI_CON);
147 	val &= ~USI_CON_RESET;
148 	writel(val, usi->regs + USI_CON);
149 	udelay(1);
150 
151 	/* Continuously provide the clock to USI IP w/o gating */
152 	if (usi->clkreq_on) {
153 		val = readl(usi->regs + USI_OPTION);
154 		val &= ~USI_OPTION_CLKSTOP_ON;
155 		val |= USI_OPTION_CLKREQ_ON;
156 		writel(val, usi->regs + USI_OPTION);
157 	}
158 
159 	clk_bulk_disable_unprepare(usi->data->num_clks, usi->clks);
160 
161 	return ret;
162 }
163 
exynos_usi_configure(struct exynos_usi * usi)164 static int exynos_usi_configure(struct exynos_usi *usi)
165 {
166 	int ret;
167 
168 	ret = exynos_usi_set_sw_conf(usi, usi->mode);
169 	if (ret)
170 		return ret;
171 
172 	if (usi->data->ver == USI_VER2)
173 		return exynos_usi_enable(usi);
174 
175 	return 0;
176 }
177 
exynos_usi_parse_dt(struct device_node * np,struct exynos_usi * usi)178 static int exynos_usi_parse_dt(struct device_node *np, struct exynos_usi *usi)
179 {
180 	int ret;
181 	u32 mode;
182 
183 	ret = of_property_read_u32(np, "samsung,mode", &mode);
184 	if (ret)
185 		return ret;
186 	if (mode < usi->data->min_mode || mode > usi->data->max_mode)
187 		return -EINVAL;
188 	usi->mode = mode;
189 
190 	usi->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg");
191 	if (IS_ERR(usi->sysreg))
192 		return PTR_ERR(usi->sysreg);
193 
194 	ret = of_property_read_u32_index(np, "samsung,sysreg", 1,
195 					 &usi->sw_conf);
196 	if (ret)
197 		return ret;
198 
199 	usi->clkreq_on = of_property_read_bool(np, "samsung,clkreq-on");
200 
201 	return 0;
202 }
203 
exynos_usi_get_clocks(struct exynos_usi * usi)204 static int exynos_usi_get_clocks(struct exynos_usi *usi)
205 {
206 	const size_t num = usi->data->num_clks;
207 	struct device *dev = usi->dev;
208 	size_t i;
209 
210 	if (num == 0)
211 		return 0;
212 
213 	usi->clks = devm_kcalloc(dev, num, sizeof(*usi->clks), GFP_KERNEL);
214 	if (!usi->clks)
215 		return -ENOMEM;
216 
217 	for (i = 0; i < num; ++i)
218 		usi->clks[i].id = usi->data->clk_names[i];
219 
220 	return devm_clk_bulk_get(dev, num, usi->clks);
221 }
222 
exynos_usi_probe(struct platform_device * pdev)223 static int exynos_usi_probe(struct platform_device *pdev)
224 {
225 	struct device *dev = &pdev->dev;
226 	struct device_node *np = dev->of_node;
227 	struct exynos_usi *usi;
228 	int ret;
229 
230 	usi = devm_kzalloc(dev, sizeof(*usi), GFP_KERNEL);
231 	if (!usi)
232 		return -ENOMEM;
233 
234 	usi->dev = dev;
235 	platform_set_drvdata(pdev, usi);
236 
237 	usi->data = of_device_get_match_data(dev);
238 	if (!usi->data)
239 		return -EINVAL;
240 
241 	ret = exynos_usi_parse_dt(np, usi);
242 	if (ret)
243 		return ret;
244 
245 	ret = exynos_usi_get_clocks(usi);
246 	if (ret)
247 		return ret;
248 
249 	if (usi->data->ver == USI_VER2) {
250 		usi->regs = devm_platform_ioremap_resource(pdev, 0);
251 		if (IS_ERR(usi->regs))
252 			return PTR_ERR(usi->regs);
253 	}
254 
255 	ret = exynos_usi_configure(usi);
256 	if (ret)
257 		return ret;
258 
259 	/* Make it possible to embed protocol nodes into USI np */
260 	return of_platform_populate(np, NULL, NULL, dev);
261 }
262 
exynos_usi_resume_noirq(struct device * dev)263 static int __maybe_unused exynos_usi_resume_noirq(struct device *dev)
264 {
265 	struct exynos_usi *usi = dev_get_drvdata(dev);
266 
267 	return exynos_usi_configure(usi);
268 }
269 
270 static const struct dev_pm_ops exynos_usi_pm = {
271 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, exynos_usi_resume_noirq)
272 };
273 
274 static struct platform_driver exynos_usi_driver = {
275 	.driver = {
276 		.name		= "exynos-usi",
277 		.pm		= &exynos_usi_pm,
278 		.of_match_table	= exynos_usi_dt_match,
279 	},
280 	.probe = exynos_usi_probe,
281 };
282 module_platform_driver(exynos_usi_driver);
283 
284 MODULE_DESCRIPTION("Samsung USI driver");
285 MODULE_AUTHOR("Sam Protsenko <semen.protsenko@linaro.org>");
286 MODULE_LICENSE("GPL");
287