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[] __maybe_unused = {
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_enabled(&pdev->dev, "iface");
182 if (IS_ERR(gsbi->hclk))
183 return PTR_ERR(gsbi->hclk);
185 writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
186 base + GSBI_CTRL_REG);
189 * modify tcsr to reflect mode and ADM CRCI mux
190 * Each gsbi contains a pair of bits, one for RX and one for TX
191 * SPI mode requires both bits cleared, otherwise they are set
194 for (i = 0; i < config->num_rows; i++) {
195 mask = config->array[i][gsbi_num - 1];
197 if (gsbi->mode == GSBI_PROT_SPI)
198 regmap_update_bits(gsbi->tcsr,
199 TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
201 regmap_update_bits(gsbi->tcsr,
202 TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
207 /* make sure the gsbi control write is not reordered */
210 platform_set_drvdata(pdev, gsbi);
212 return of_platform_populate(node, NULL, NULL, &pdev->dev);
215 static void gsbi_remove(struct platform_device *pdev)
217 struct gsbi_info *gsbi = platform_get_drvdata(pdev);
219 clk_disable_unprepare(gsbi->hclk);
222 static const struct of_device_id gsbi_dt_match[] = {
223 { .compatible = "qcom,gsbi-v1.0.0", },
227 MODULE_DEVICE_TABLE(of, gsbi_dt_match);
229 static struct platform_driver gsbi_driver = {
232 .of_match_table = gsbi_dt_match,
235 .remove = gsbi_remove,
238 module_platform_driver(gsbi_driver);
241 MODULE_DESCRIPTION("QCOM GSBI driver");
242 MODULE_LICENSE("GPL v2");