1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2014, The Linux foundation. All rights reserved. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/err.h> 8 #include <linux/io.h> 9 #include <linux/module.h> 10 #include <linux/of.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> 16 17 #define GSBI_CTRL_REG 0x0000 18 #define GSBI_PROTOCOL_SHIFT 4 19 #define MAX_GSBI 12 20 21 #define TCSR_ADM_CRCI_BASE 0x70 22 23 struct crci_config { 24 u32 num_rows; 25 const u32 (*array)[MAX_GSBI]; 26 }; 27 28 static const u32 crci_ipq8064[][MAX_GSBI] = { 29 { 30 0x000003, 0x00000c, 0x000030, 0x0000c0, 31 0x000300, 0x000c00, 0x003000, 0x00c000, 32 0x030000, 0x0c0000, 0x300000, 0xc00000 33 }, 34 { 35 0x000003, 0x00000c, 0x000030, 0x0000c0, 36 0x000300, 0x000c00, 0x003000, 0x00c000, 37 0x030000, 0x0c0000, 0x300000, 0xc00000 38 }, 39 }; 40 41 static const struct crci_config config_ipq8064 = { 42 .num_rows = ARRAY_SIZE(crci_ipq8064), 43 .array = crci_ipq8064, 44 }; 45 46 static const unsigned int crci_apq8064[][MAX_GSBI] = { 47 { 48 0x001800, 0x006000, 0x000030, 0x0000c0, 49 0x000300, 0x000400, 0x000000, 0x000000, 50 0x000000, 0x000000, 0x000000, 0x000000 51 }, 52 { 53 0x000000, 0x000000, 0x000000, 0x000000, 54 0x000000, 0x000020, 0x0000c0, 0x000000, 55 0x000000, 0x000000, 0x000000, 0x000000 56 }, 57 }; 58 59 static const struct crci_config config_apq8064 = { 60 .num_rows = ARRAY_SIZE(crci_apq8064), 61 .array = crci_apq8064, 62 }; 63 64 static const unsigned int crci_msm8960[][MAX_GSBI] = { 65 { 66 0x000003, 0x00000c, 0x000030, 0x0000c0, 67 0x000300, 0x000400, 0x000000, 0x000000, 68 0x000000, 0x000000, 0x000000, 0x000000 69 }, 70 { 71 0x000000, 0x000000, 0x000000, 0x000000, 72 0x000000, 0x000020, 0x0000c0, 0x000300, 73 0x001800, 0x006000, 0x000000, 0x000000 74 }, 75 }; 76 77 static const struct crci_config config_msm8960 = { 78 .num_rows = ARRAY_SIZE(crci_msm8960), 79 .array = crci_msm8960, 80 }; 81 82 static const unsigned int crci_msm8660[][MAX_GSBI] = { 83 { /* ADM 0 - B */ 84 0x000003, 0x00000c, 0x000030, 0x0000c0, 85 0x000300, 0x000c00, 0x003000, 0x00c000, 86 0x030000, 0x0c0000, 0x300000, 0xc00000 87 }, 88 { /* ADM 0 - B */ 89 0x000003, 0x00000c, 0x000030, 0x0000c0, 90 0x000300, 0x000c00, 0x003000, 0x00c000, 91 0x030000, 0x0c0000, 0x300000, 0xc00000 92 }, 93 { /* ADM 1 - A */ 94 0x000003, 0x00000c, 0x000030, 0x0000c0, 95 0x000300, 0x000c00, 0x003000, 0x00c000, 96 0x030000, 0x0c0000, 0x300000, 0xc00000 97 }, 98 { /* ADM 1 - B */ 99 0x000003, 0x00000c, 0x000030, 0x0000c0, 100 0x000300, 0x000c00, 0x003000, 0x00c000, 101 0x030000, 0x0c0000, 0x300000, 0xc00000 102 }, 103 }; 104 105 static const struct crci_config config_msm8660 = { 106 .num_rows = ARRAY_SIZE(crci_msm8660), 107 .array = crci_msm8660, 108 }; 109 110 struct gsbi_info { 111 struct clk *hclk; 112 u32 mode; 113 u32 crci; 114 struct regmap *tcsr; 115 }; 116 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}, 122 { }, 123 }; 124 125 static int gsbi_probe(struct platform_device *pdev) 126 { 127 struct device_node *node = pdev->dev.of_node; 128 struct device_node *tcsr_node; 129 const struct of_device_id *match; 130 void __iomem *base; 131 struct gsbi_info *gsbi; 132 int i; 133 u32 mask, gsbi_num; 134 const struct crci_config *config = NULL; 135 136 gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL); 137 138 if (!gsbi) 139 return -ENOMEM; 140 141 base = devm_platform_ioremap_resource(pdev, 0); 142 if (IS_ERR(base)) 143 return PTR_ERR(base); 144 145 /* get the tcsr node and setup the config and regmap */ 146 gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr"); 147 148 if (!IS_ERR(gsbi->tcsr)) { 149 tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0); 150 if (tcsr_node) { 151 match = of_match_node(tcsr_dt_match, tcsr_node); 152 if (match) 153 config = match->data; 154 else 155 dev_warn(&pdev->dev, "no matching TCSR\n"); 156 157 of_node_put(tcsr_node); 158 } 159 } 160 161 if (of_property_read_u32(node, "cell-index", &gsbi_num)) { 162 dev_err(&pdev->dev, "missing cell-index\n"); 163 return -EINVAL; 164 } 165 166 if (gsbi_num < 1 || gsbi_num > MAX_GSBI) { 167 dev_err(&pdev->dev, "invalid cell-index\n"); 168 return -EINVAL; 169 } 170 171 if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) { 172 dev_err(&pdev->dev, "missing mode configuration\n"); 173 return -EINVAL; 174 } 175 176 /* not required, so default to 0 if not present */ 177 of_property_read_u32(node, "qcom,crci", &gsbi->crci); 178 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); 184 185 writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci, 186 base + GSBI_CTRL_REG); 187 188 /* 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 192 */ 193 if (config) { 194 for (i = 0; i < config->num_rows; i++) { 195 mask = config->array[i][gsbi_num - 1]; 196 197 if (gsbi->mode == GSBI_PROT_SPI) 198 regmap_update_bits(gsbi->tcsr, 199 TCSR_ADM_CRCI_BASE + 4 * i, mask, 0); 200 else 201 regmap_update_bits(gsbi->tcsr, 202 TCSR_ADM_CRCI_BASE + 4 * i, mask, mask); 203 204 } 205 } 206 207 /* make sure the gsbi control write is not reordered */ 208 wmb(); 209 210 platform_set_drvdata(pdev, gsbi); 211 212 return of_platform_populate(node, NULL, NULL, &pdev->dev); 213 } 214 215 static int gsbi_remove(struct platform_device *pdev) 216 { 217 struct gsbi_info *gsbi = platform_get_drvdata(pdev); 218 219 clk_disable_unprepare(gsbi->hclk); 220 221 return 0; 222 } 223 224 static const struct of_device_id gsbi_dt_match[] = { 225 { .compatible = "qcom,gsbi-v1.0.0", }, 226 { }, 227 }; 228 229 MODULE_DEVICE_TABLE(of, gsbi_dt_match); 230 231 static struct platform_driver gsbi_driver = { 232 .driver = { 233 .name = "gsbi", 234 .of_match_table = gsbi_dt_match, 235 }, 236 .probe = gsbi_probe, 237 .remove = gsbi_remove, 238 }; 239 240 module_platform_driver(gsbi_driver); 241 242 MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); 243 MODULE_DESCRIPTION("QCOM GSBI driver"); 244 MODULE_LICENSE("GPL v2"); 245