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[] = { 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 struct resource *res; 131 void __iomem *base; 132 struct gsbi_info *gsbi; 133 int i, ret; 134 u32 mask, gsbi_num; 135 const struct crci_config *config = NULL; 136 137 gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL); 138 139 if (!gsbi) 140 return -ENOMEM; 141 142 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 143 base = devm_ioremap_resource(&pdev->dev, res); 144 if (IS_ERR(base)) 145 return PTR_ERR(base); 146 147 /* get the tcsr node and setup the config and regmap */ 148 gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr"); 149 150 if (!IS_ERR(gsbi->tcsr)) { 151 tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0); 152 if (tcsr_node) { 153 match = of_match_node(tcsr_dt_match, tcsr_node); 154 if (match) 155 config = match->data; 156 else 157 dev_warn(&pdev->dev, "no matching TCSR\n"); 158 159 of_node_put(tcsr_node); 160 } 161 } 162 163 if (of_property_read_u32(node, "cell-index", &gsbi_num)) { 164 dev_err(&pdev->dev, "missing cell-index\n"); 165 return -EINVAL; 166 } 167 168 if (gsbi_num < 1 || gsbi_num > MAX_GSBI) { 169 dev_err(&pdev->dev, "invalid cell-index\n"); 170 return -EINVAL; 171 } 172 173 if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) { 174 dev_err(&pdev->dev, "missing mode configuration\n"); 175 return -EINVAL; 176 } 177 178 /* not required, so default to 0 if not present */ 179 of_property_read_u32(node, "qcom,crci", &gsbi->crci); 180 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); 186 187 clk_prepare_enable(gsbi->hclk); 188 189 writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci, 190 base + GSBI_CTRL_REG); 191 192 /* 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 196 */ 197 if (config) { 198 for (i = 0; i < config->num_rows; i++) { 199 mask = config->array[i][gsbi_num - 1]; 200 201 if (gsbi->mode == GSBI_PROT_SPI) 202 regmap_update_bits(gsbi->tcsr, 203 TCSR_ADM_CRCI_BASE + 4 * i, mask, 0); 204 else 205 regmap_update_bits(gsbi->tcsr, 206 TCSR_ADM_CRCI_BASE + 4 * i, mask, mask); 207 208 } 209 } 210 211 /* make sure the gsbi control write is not reordered */ 212 wmb(); 213 214 platform_set_drvdata(pdev, gsbi); 215 216 ret = of_platform_populate(node, NULL, NULL, &pdev->dev); 217 if (ret) 218 clk_disable_unprepare(gsbi->hclk); 219 return ret; 220 } 221 222 static int gsbi_remove(struct platform_device *pdev) 223 { 224 struct gsbi_info *gsbi = platform_get_drvdata(pdev); 225 226 clk_disable_unprepare(gsbi->hclk); 227 228 return 0; 229 } 230 231 static const struct of_device_id gsbi_dt_match[] = { 232 { .compatible = "qcom,gsbi-v1.0.0", }, 233 { }, 234 }; 235 236 MODULE_DEVICE_TABLE(of, gsbi_dt_match); 237 238 static struct platform_driver gsbi_driver = { 239 .driver = { 240 .name = "gsbi", 241 .of_match_table = gsbi_dt_match, 242 }, 243 .probe = gsbi_probe, 244 .remove = gsbi_remove, 245 }; 246 247 module_platform_driver(gsbi_driver); 248 249 MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); 250 MODULE_DESCRIPTION("QCOM GSBI driver"); 251 MODULE_LICENSE("GPL v2"); 252