2 * Copyright (c) 2014, The Linux foundation. All rights reserved.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License rev 2 and
6 * only rev 2 as published by the free Software foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <linux/clk.h>
15 #include <linux/err.h>
17 #include <linux/module.h>
19 #include <linux/of_platform.h>
20 #include <linux/platform_device.h>
21 #include <linux/regmap.h>
22 #include <linux/mfd/syscon.h>
23 #include <dt-bindings/soc/qcom,gsbi.h>
25 #define GSBI_CTRL_REG 0x0000
26 #define GSBI_PROTOCOL_SHIFT 4
29 #define TCSR_ADM_CRCI_BASE 0x70
33 const u32 (*array
)[MAX_GSBI
];
36 static const u32 crci_ipq8064
[][MAX_GSBI
] = {
38 0x000003, 0x00000c, 0x000030, 0x0000c0,
39 0x000300, 0x000c00, 0x003000, 0x00c000,
40 0x030000, 0x0c0000, 0x300000, 0xc00000
43 0x000003, 0x00000c, 0x000030, 0x0000c0,
44 0x000300, 0x000c00, 0x003000, 0x00c000,
45 0x030000, 0x0c0000, 0x300000, 0xc00000
49 static const struct crci_config config_ipq8064
= {
50 .num_rows
= ARRAY_SIZE(crci_ipq8064
),
51 .array
= crci_ipq8064
,
54 static const unsigned int crci_apq8064
[][MAX_GSBI
] = {
56 0x001800, 0x006000, 0x000030, 0x0000c0,
57 0x000300, 0x000400, 0x000000, 0x000000,
58 0x000000, 0x000000, 0x000000, 0x000000
61 0x000000, 0x000000, 0x000000, 0x000000,
62 0x000000, 0x000020, 0x0000c0, 0x000000,
63 0x000000, 0x000000, 0x000000, 0x000000
67 static const struct crci_config config_apq8064
= {
68 .num_rows
= ARRAY_SIZE(crci_apq8064
),
69 .array
= crci_apq8064
,
72 static const unsigned int crci_msm8960
[][MAX_GSBI
] = {
74 0x000003, 0x00000c, 0x000030, 0x0000c0,
75 0x000300, 0x000400, 0x000000, 0x000000,
76 0x000000, 0x000000, 0x000000, 0x000000
79 0x000000, 0x000000, 0x000000, 0x000000,
80 0x000000, 0x000020, 0x0000c0, 0x000300,
81 0x001800, 0x006000, 0x000000, 0x000000
85 static const struct crci_config config_msm8960
= {
86 .num_rows
= ARRAY_SIZE(crci_msm8960
),
87 .array
= crci_msm8960
,
90 static const unsigned int crci_msm8660
[][MAX_GSBI
] = {
92 0x000003, 0x00000c, 0x000030, 0x0000c0,
93 0x000300, 0x000c00, 0x003000, 0x00c000,
94 0x030000, 0x0c0000, 0x300000, 0xc00000
97 0x000003, 0x00000c, 0x000030, 0x0000c0,
98 0x000300, 0x000c00, 0x003000, 0x00c000,
99 0x030000, 0x0c0000, 0x300000, 0xc00000
102 0x000003, 0x00000c, 0x000030, 0x0000c0,
103 0x000300, 0x000c00, 0x003000, 0x00c000,
104 0x030000, 0x0c0000, 0x300000, 0xc00000
107 0x000003, 0x00000c, 0x000030, 0x0000c0,
108 0x000300, 0x000c00, 0x003000, 0x00c000,
109 0x030000, 0x0c0000, 0x300000, 0xc00000
113 static const struct crci_config config_msm8660
= {
114 .num_rows
= ARRAY_SIZE(crci_msm8660
),
115 .array
= crci_msm8660
,
125 static const struct of_device_id tcsr_dt_match
[] = {
126 { .compatible
= "qcom,tcsr-ipq8064", .data
= &config_ipq8064
},
127 { .compatible
= "qcom,tcsr-apq8064", .data
= &config_apq8064
},
128 { .compatible
= "qcom,tcsr-msm8960", .data
= &config_msm8960
},
129 { .compatible
= "qcom,tcsr-msm8660", .data
= &config_msm8660
},
133 static int gsbi_probe(struct platform_device
*pdev
)
135 struct device_node
*node
= pdev
->dev
.of_node
;
136 struct device_node
*tcsr_node
;
137 const struct of_device_id
*match
;
138 struct resource
*res
;
140 struct gsbi_info
*gsbi
;
143 const struct crci_config
*config
= NULL
;
145 gsbi
= devm_kzalloc(&pdev
->dev
, sizeof(*gsbi
), GFP_KERNEL
);
150 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
151 base
= devm_ioremap_resource(&pdev
->dev
, res
);
153 return PTR_ERR(base
);
155 /* get the tcsr node and setup the config and regmap */
156 gsbi
->tcsr
= syscon_regmap_lookup_by_phandle(node
, "syscon-tcsr");
158 if (!IS_ERR(gsbi
->tcsr
)) {
159 tcsr_node
= of_parse_phandle(node
, "syscon-tcsr", 0);
161 match
= of_match_node(tcsr_dt_match
, tcsr_node
);
163 config
= match
->data
;
165 dev_warn(&pdev
->dev
, "no matching TCSR\n");
167 of_node_put(tcsr_node
);
171 if (of_property_read_u32(node
, "cell-index", &gsbi_num
)) {
172 dev_err(&pdev
->dev
, "missing cell-index\n");
176 if (gsbi_num
< 1 || gsbi_num
> MAX_GSBI
) {
177 dev_err(&pdev
->dev
, "invalid cell-index\n");
181 if (of_property_read_u32(node
, "qcom,mode", &gsbi
->mode
)) {
182 dev_err(&pdev
->dev
, "missing mode configuration\n");
186 /* not required, so default to 0 if not present */
187 of_property_read_u32(node
, "qcom,crci", &gsbi
->crci
);
189 dev_info(&pdev
->dev
, "GSBI port protocol: %d crci: %d\n",
190 gsbi
->mode
, gsbi
->crci
);
191 gsbi
->hclk
= devm_clk_get(&pdev
->dev
, "iface");
192 if (IS_ERR(gsbi
->hclk
))
193 return PTR_ERR(gsbi
->hclk
);
195 clk_prepare_enable(gsbi
->hclk
);
197 writel_relaxed((gsbi
->mode
<< GSBI_PROTOCOL_SHIFT
) | gsbi
->crci
,
198 base
+ GSBI_CTRL_REG
);
201 * modify tcsr to reflect mode and ADM CRCI mux
202 * Each gsbi contains a pair of bits, one for RX and one for TX
203 * SPI mode requires both bits cleared, otherwise they are set
206 for (i
= 0; i
< config
->num_rows
; i
++) {
207 mask
= config
->array
[i
][gsbi_num
- 1];
209 if (gsbi
->mode
== GSBI_PROT_SPI
)
210 regmap_update_bits(gsbi
->tcsr
,
211 TCSR_ADM_CRCI_BASE
+ 4 * i
, mask
, 0);
213 regmap_update_bits(gsbi
->tcsr
,
214 TCSR_ADM_CRCI_BASE
+ 4 * i
, mask
, mask
);
219 /* make sure the gsbi control write is not reordered */
222 platform_set_drvdata(pdev
, gsbi
);
224 return of_platform_populate(node
, NULL
, NULL
, &pdev
->dev
);
227 static int gsbi_remove(struct platform_device
*pdev
)
229 struct gsbi_info
*gsbi
= platform_get_drvdata(pdev
);
231 clk_disable_unprepare(gsbi
->hclk
);
236 static const struct of_device_id gsbi_dt_match
[] = {
237 { .compatible
= "qcom,gsbi-v1.0.0", },
241 MODULE_DEVICE_TABLE(of
, gsbi_dt_match
);
243 static struct platform_driver gsbi_driver
= {
246 .of_match_table
= gsbi_dt_match
,
249 .remove
= gsbi_remove
,
252 module_platform_driver(gsbi_driver
);
254 MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
255 MODULE_DESCRIPTION("QCOM GSBI driver");
256 MODULE_LICENSE("GPL v2");