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