1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2014, The Linux foundation. All rights reserved.
9 #include <linux/module.h>
11 #include <linux/of_platform.h>
12 #include <linux/platform_device.h>
13 #include <linux/regmap.h>
14 #include <linux/mfd/syscon.h>
15 #include <dt-bindings/soc/qcom,gsbi.h>
17 #define GSBI_CTRL_REG 0x0000
18 #define GSBI_PROTOCOL_SHIFT 4
21 #define TCSR_ADM_CRCI_BASE 0x70
25 const u32 (*array)[MAX_GSBI];
28 static const u32 crci_ipq8064[][MAX_GSBI] = {
30 0x000003, 0x00000c, 0x000030, 0x0000c0,
31 0x000300, 0x000c00, 0x003000, 0x00c000,
32 0x030000, 0x0c0000, 0x300000, 0xc00000
35 0x000003, 0x00000c, 0x000030, 0x0000c0,
36 0x000300, 0x000c00, 0x003000, 0x00c000,
37 0x030000, 0x0c0000, 0x300000, 0xc00000
41 static const struct crci_config config_ipq8064 = {
42 .num_rows = ARRAY_SIZE(crci_ipq8064),
43 .array = crci_ipq8064,
46 static const unsigned int crci_apq8064[][MAX_GSBI] = {
48 0x001800, 0x006000, 0x000030, 0x0000c0,
49 0x000300, 0x000400, 0x000000, 0x000000,
50 0x000000, 0x000000, 0x000000, 0x000000
53 0x000000, 0x000000, 0x000000, 0x000000,
54 0x000000, 0x000020, 0x0000c0, 0x000000,
55 0x000000, 0x000000, 0x000000, 0x000000
59 static const struct crci_config config_apq8064 = {
60 .num_rows = ARRAY_SIZE(crci_apq8064),
61 .array = crci_apq8064,
64 static const unsigned int crci_msm8960[][MAX_GSBI] = {
66 0x000003, 0x00000c, 0x000030, 0x0000c0,
67 0x000300, 0x000400, 0x000000, 0x000000,
68 0x000000, 0x000000, 0x000000, 0x000000
71 0x000000, 0x000000, 0x000000, 0x000000,
72 0x000000, 0x000020, 0x0000c0, 0x000300,
73 0x001800, 0x006000, 0x000000, 0x000000
77 static const struct crci_config config_msm8960 = {
78 .num_rows = ARRAY_SIZE(crci_msm8960),
79 .array = crci_msm8960,
82 static const unsigned int crci_msm8660[][MAX_GSBI] = {
84 0x000003, 0x00000c, 0x000030, 0x0000c0,
85 0x000300, 0x000c00, 0x003000, 0x00c000,
86 0x030000, 0x0c0000, 0x300000, 0xc00000
89 0x000003, 0x00000c, 0x000030, 0x0000c0,
90 0x000300, 0x000c00, 0x003000, 0x00c000,
91 0x030000, 0x0c0000, 0x300000, 0xc00000
94 0x000003, 0x00000c, 0x000030, 0x0000c0,
95 0x000300, 0x000c00, 0x003000, 0x00c000,
96 0x030000, 0x0c0000, 0x300000, 0xc00000
99 0x000003, 0x00000c, 0x000030, 0x0000c0,
100 0x000300, 0x000c00, 0x003000, 0x00c000,
101 0x030000, 0x0c0000, 0x300000, 0xc00000
105 static const struct crci_config config_msm8660 = {
106 .num_rows = ARRAY_SIZE(crci_msm8660),
107 .array = crci_msm8660,
117 static const struct of_device_id tcsr_dt_match[] = {
118 { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
119 { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
120 { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
121 { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
125 static int gsbi_probe(struct platform_device *pdev)
127 struct device_node *node = pdev->dev.of_node;
128 struct device_node *tcsr_node;
129 const struct of_device_id *match;
131 struct gsbi_info *gsbi;
134 const struct crci_config *config = NULL;
136 gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
141 base = devm_platform_ioremap_resource(pdev, 0);
143 return PTR_ERR(base);
145 /* get the tcsr node and setup the config and regmap */
146 gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
148 if (!IS_ERR(gsbi->tcsr)) {
149 tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
151 match = of_match_node(tcsr_dt_match, tcsr_node);
153 config = match->data;
155 dev_warn(&pdev->dev, "no matching TCSR\n");
157 of_node_put(tcsr_node);
161 if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
162 dev_err(&pdev->dev, "missing cell-index\n");
166 if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
167 dev_err(&pdev->dev, "invalid cell-index\n");
171 if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
172 dev_err(&pdev->dev, "missing mode configuration\n");
176 /* not required, so default to 0 if not present */
177 of_property_read_u32(node, "qcom,crci", &gsbi->crci);
179 dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n",
180 gsbi->mode, gsbi->crci);
181 gsbi->hclk = devm_clk_get(&pdev->dev, "iface");
182 if (IS_ERR(gsbi->hclk))
183 return PTR_ERR(gsbi->hclk);
185 clk_prepare_enable(gsbi->hclk);
187 writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
188 base + GSBI_CTRL_REG);
191 * modify tcsr to reflect mode and ADM CRCI mux
192 * Each gsbi contains a pair of bits, one for RX and one for TX
193 * SPI mode requires both bits cleared, otherwise they are set
196 for (i = 0; i < config->num_rows; i++) {
197 mask = config->array[i][gsbi_num - 1];
199 if (gsbi->mode == GSBI_PROT_SPI)
200 regmap_update_bits(gsbi->tcsr,
201 TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
203 regmap_update_bits(gsbi->tcsr,
204 TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
209 /* make sure the gsbi control write is not reordered */
212 platform_set_drvdata(pdev, gsbi);
214 ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
216 clk_disable_unprepare(gsbi->hclk);
220 static int gsbi_remove(struct platform_device *pdev)
222 struct gsbi_info *gsbi = platform_get_drvdata(pdev);
224 clk_disable_unprepare(gsbi->hclk);
229 static const struct of_device_id gsbi_dt_match[] = {
230 { .compatible = "qcom,gsbi-v1.0.0", },
234 MODULE_DEVICE_TABLE(of, gsbi_dt_match);
236 static struct platform_driver gsbi_driver = {
239 .of_match_table = gsbi_dt_match,
242 .remove = gsbi_remove,
245 module_platform_driver(gsbi_driver);
247 MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
248 MODULE_DESCRIPTION("QCOM GSBI driver");
249 MODULE_LICENSE("GPL v2");