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
;
130 struct resource
*res
;
132 struct gsbi_info
*gsbi
;
135 const struct crci_config
*config
= NULL
;
137 gsbi
= devm_kzalloc(&pdev
->dev
, sizeof(*gsbi
), GFP_KERNEL
);
142 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
143 base
= devm_ioremap_resource(&pdev
->dev
, res
);
145 return PTR_ERR(base
);
147 /* get the tcsr node and setup the config and regmap */
148 gsbi
->tcsr
= syscon_regmap_lookup_by_phandle(node
, "syscon-tcsr");
150 if (!IS_ERR(gsbi
->tcsr
)) {
151 tcsr_node
= of_parse_phandle(node
, "syscon-tcsr", 0);
153 match
= of_match_node(tcsr_dt_match
, tcsr_node
);
155 config
= match
->data
;
157 dev_warn(&pdev
->dev
, "no matching TCSR\n");
159 of_node_put(tcsr_node
);
163 if (of_property_read_u32(node
, "cell-index", &gsbi_num
)) {
164 dev_err(&pdev
->dev
, "missing cell-index\n");
168 if (gsbi_num
< 1 || gsbi_num
> MAX_GSBI
) {
169 dev_err(&pdev
->dev
, "invalid cell-index\n");
173 if (of_property_read_u32(node
, "qcom,mode", &gsbi
->mode
)) {
174 dev_err(&pdev
->dev
, "missing mode configuration\n");
178 /* not required, so default to 0 if not present */
179 of_property_read_u32(node
, "qcom,crci", &gsbi
->crci
);
181 dev_info(&pdev
->dev
, "GSBI port protocol: %d crci: %d\n",
182 gsbi
->mode
, gsbi
->crci
);
183 gsbi
->hclk
= devm_clk_get(&pdev
->dev
, "iface");
184 if (IS_ERR(gsbi
->hclk
))
185 return PTR_ERR(gsbi
->hclk
);
187 clk_prepare_enable(gsbi
->hclk
);
189 writel_relaxed((gsbi
->mode
<< GSBI_PROTOCOL_SHIFT
) | gsbi
->crci
,
190 base
+ GSBI_CTRL_REG
);
193 * modify tcsr to reflect mode and ADM CRCI mux
194 * Each gsbi contains a pair of bits, one for RX and one for TX
195 * SPI mode requires both bits cleared, otherwise they are set
198 for (i
= 0; i
< config
->num_rows
; i
++) {
199 mask
= config
->array
[i
][gsbi_num
- 1];
201 if (gsbi
->mode
== GSBI_PROT_SPI
)
202 regmap_update_bits(gsbi
->tcsr
,
203 TCSR_ADM_CRCI_BASE
+ 4 * i
, mask
, 0);
205 regmap_update_bits(gsbi
->tcsr
,
206 TCSR_ADM_CRCI_BASE
+ 4 * i
, mask
, mask
);
211 /* make sure the gsbi control write is not reordered */
214 platform_set_drvdata(pdev
, gsbi
);
216 ret
= of_platform_populate(node
, NULL
, NULL
, &pdev
->dev
);
218 clk_disable_unprepare(gsbi
->hclk
);
222 static int gsbi_remove(struct platform_device
*pdev
)
224 struct gsbi_info
*gsbi
= platform_get_drvdata(pdev
);
226 clk_disable_unprepare(gsbi
->hclk
);
231 static const struct of_device_id gsbi_dt_match
[] = {
232 { .compatible
= "qcom,gsbi-v1.0.0", },
236 MODULE_DEVICE_TABLE(of
, gsbi_dt_match
);
238 static struct platform_driver gsbi_driver
= {
241 .of_match_table
= gsbi_dt_match
,
244 .remove
= gsbi_remove
,
247 module_platform_driver(gsbi_driver
);
249 MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
250 MODULE_DESCRIPTION("QCOM GSBI driver");
251 MODULE_LICENSE("GPL v2");