qcom_gsbi.c (6118B)
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 23struct crci_config { 24 u32 num_rows; 25 const u32 (*array)[MAX_GSBI]; 26}; 27 28static 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 41static const struct crci_config config_ipq8064 = { 42 .num_rows = ARRAY_SIZE(crci_ipq8064), 43 .array = crci_ipq8064, 44}; 45 46static 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 59static const struct crci_config config_apq8064 = { 60 .num_rows = ARRAY_SIZE(crci_apq8064), 61 .array = crci_apq8064, 62}; 63 64static 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 77static const struct crci_config config_msm8960 = { 78 .num_rows = ARRAY_SIZE(crci_msm8960), 79 .array = crci_msm8960, 80}; 81 82static 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 105static const struct crci_config config_msm8660 = { 106 .num_rows = ARRAY_SIZE(crci_msm8660), 107 .array = crci_msm8660, 108}; 109 110struct gsbi_info { 111 struct clk *hclk; 112 u32 mode; 113 u32 crci; 114 struct regmap *tcsr; 115}; 116 117static 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 125static 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, ret; 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(&pdev->dev, "iface"); 182 if (IS_ERR(gsbi->hclk)) 183 return PTR_ERR(gsbi->hclk); 184 185 clk_prepare_enable(gsbi->hclk); 186 187 writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci, 188 base + GSBI_CTRL_REG); 189 190 /* 191 * modify tcsr to reflect mode and ADM CRCI mux 192 * Each gsbi contains a pair of bits, one for RX and one for TX 193 * SPI mode requires both bits cleared, otherwise they are set 194 */ 195 if (config) { 196 for (i = 0; i < config->num_rows; i++) { 197 mask = config->array[i][gsbi_num - 1]; 198 199 if (gsbi->mode == GSBI_PROT_SPI) 200 regmap_update_bits(gsbi->tcsr, 201 TCSR_ADM_CRCI_BASE + 4 * i, mask, 0); 202 else 203 regmap_update_bits(gsbi->tcsr, 204 TCSR_ADM_CRCI_BASE + 4 * i, mask, mask); 205 206 } 207 } 208 209 /* make sure the gsbi control write is not reordered */ 210 wmb(); 211 212 platform_set_drvdata(pdev, gsbi); 213 214 ret = of_platform_populate(node, NULL, NULL, &pdev->dev); 215 if (ret) 216 clk_disable_unprepare(gsbi->hclk); 217 return ret; 218} 219 220static int gsbi_remove(struct platform_device *pdev) 221{ 222 struct gsbi_info *gsbi = platform_get_drvdata(pdev); 223 224 clk_disable_unprepare(gsbi->hclk); 225 226 return 0; 227} 228 229static const struct of_device_id gsbi_dt_match[] = { 230 { .compatible = "qcom,gsbi-v1.0.0", }, 231 { }, 232}; 233 234MODULE_DEVICE_TABLE(of, gsbi_dt_match); 235 236static struct platform_driver gsbi_driver = { 237 .driver = { 238 .name = "gsbi", 239 .of_match_table = gsbi_dt_match, 240 }, 241 .probe = gsbi_probe, 242 .remove = gsbi_remove, 243}; 244 245module_platform_driver(gsbi_driver); 246 247MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); 248MODULE_DESCRIPTION("QCOM GSBI driver"); 249MODULE_LICENSE("GPL v2");