ssbi.c (7610B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. 3 * Copyright (c) 2010, Google Inc. 4 * 5 * Original authors: Code Aurora Forum 6 * 7 * Author: Dima Zavin <dima@android.com> 8 * - Largely rewritten from original to not be an i2c driver. 9 */ 10 11#define pr_fmt(fmt) "%s: " fmt, __func__ 12 13#include <linux/delay.h> 14#include <linux/err.h> 15#include <linux/io.h> 16#include <linux/kernel.h> 17#include <linux/platform_device.h> 18#include <linux/slab.h> 19#include <linux/ssbi.h> 20#include <linux/module.h> 21#include <linux/of.h> 22#include <linux/of_device.h> 23 24/* SSBI 2.0 controller registers */ 25#define SSBI2_CMD 0x0008 26#define SSBI2_RD 0x0010 27#define SSBI2_STATUS 0x0014 28#define SSBI2_MODE2 0x001C 29 30/* SSBI_CMD fields */ 31#define SSBI_CMD_RDWRN (1 << 24) 32 33/* SSBI_STATUS fields */ 34#define SSBI_STATUS_RD_READY (1 << 2) 35#define SSBI_STATUS_READY (1 << 1) 36#define SSBI_STATUS_MCHN_BUSY (1 << 0) 37 38/* SSBI_MODE2 fields */ 39#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04 40#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT) 41 42#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \ 43 (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \ 44 SSBI_MODE2_REG_ADDR_15_8_MASK)) 45 46/* SSBI PMIC Arbiter command registers */ 47#define SSBI_PA_CMD 0x0000 48#define SSBI_PA_RD_STATUS 0x0004 49 50/* SSBI_PA_CMD fields */ 51#define SSBI_PA_CMD_RDWRN (1 << 24) 52#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/ 53 54/* SSBI_PA_RD_STATUS fields */ 55#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27) 56#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26) 57 58#define SSBI_TIMEOUT_US 100 59 60enum ssbi_controller_type { 61 MSM_SBI_CTRL_SSBI = 0, 62 MSM_SBI_CTRL_SSBI2, 63 MSM_SBI_CTRL_PMIC_ARBITER, 64}; 65 66struct ssbi { 67 struct device *slave; 68 void __iomem *base; 69 spinlock_t lock; 70 enum ssbi_controller_type controller_type; 71 int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); 72 int (*write)(struct ssbi *, u16 addr, const u8 *buf, int len); 73}; 74 75static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg) 76{ 77 return readl(ssbi->base + reg); 78} 79 80static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg) 81{ 82 writel(val, ssbi->base + reg); 83} 84 85/* 86 * Via private exchange with one of the original authors, the hardware 87 * should generally finish a transaction in about 5us. The worst 88 * case, is when using the arbiter and both other CPUs have just 89 * started trying to use the SSBI bus will result in a time of about 90 * 20us. It should never take longer than this. 91 * 92 * As such, this wait merely spins, with a udelay. 93 */ 94static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask) 95{ 96 u32 timeout = SSBI_TIMEOUT_US; 97 u32 val; 98 99 while (timeout--) { 100 val = ssbi_readl(ssbi, SSBI2_STATUS); 101 if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) 102 return 0; 103 udelay(1); 104 } 105 106 return -ETIMEDOUT; 107} 108 109static int 110ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) 111{ 112 u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); 113 int ret = 0; 114 115 if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { 116 u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); 117 mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); 118 ssbi_writel(ssbi, mode2, SSBI2_MODE2); 119 } 120 121 while (len) { 122 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); 123 if (ret) 124 goto err; 125 126 ssbi_writel(ssbi, cmd, SSBI2_CMD); 127 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0); 128 if (ret) 129 goto err; 130 *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; 131 len--; 132 } 133 134err: 135 return ret; 136} 137 138static int 139ssbi_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len) 140{ 141 int ret = 0; 142 143 if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { 144 u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); 145 mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); 146 ssbi_writel(ssbi, mode2, SSBI2_MODE2); 147 } 148 149 while (len) { 150 ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); 151 if (ret) 152 goto err; 153 154 ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD); 155 ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY); 156 if (ret) 157 goto err; 158 buf++; 159 len--; 160 } 161 162err: 163 return ret; 164} 165 166/* 167 * See ssbi_wait_mask for an explanation of the time and the 168 * busywait. 169 */ 170static inline int 171ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data) 172{ 173 u32 timeout = SSBI_TIMEOUT_US; 174 u32 rd_status = 0; 175 176 ssbi_writel(ssbi, cmd, SSBI_PA_CMD); 177 178 while (timeout--) { 179 rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS); 180 181 if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) 182 return -EPERM; 183 184 if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) { 185 if (data) 186 *data = rd_status & 0xff; 187 return 0; 188 } 189 udelay(1); 190 } 191 192 return -ETIMEDOUT; 193} 194 195static int 196ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) 197{ 198 u32 cmd; 199 int ret = 0; 200 201 cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8; 202 203 while (len) { 204 ret = ssbi_pa_transfer(ssbi, cmd, buf); 205 if (ret) 206 goto err; 207 buf++; 208 len--; 209 } 210 211err: 212 return ret; 213} 214 215static int 216ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len) 217{ 218 u32 cmd; 219 int ret = 0; 220 221 while (len) { 222 cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf; 223 ret = ssbi_pa_transfer(ssbi, cmd, NULL); 224 if (ret) 225 goto err; 226 buf++; 227 len--; 228 } 229 230err: 231 return ret; 232} 233 234int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) 235{ 236 struct ssbi *ssbi = dev_get_drvdata(dev); 237 unsigned long flags; 238 int ret; 239 240 spin_lock_irqsave(&ssbi->lock, flags); 241 ret = ssbi->read(ssbi, addr, buf, len); 242 spin_unlock_irqrestore(&ssbi->lock, flags); 243 244 return ret; 245} 246EXPORT_SYMBOL_GPL(ssbi_read); 247 248int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len) 249{ 250 struct ssbi *ssbi = dev_get_drvdata(dev); 251 unsigned long flags; 252 int ret; 253 254 spin_lock_irqsave(&ssbi->lock, flags); 255 ret = ssbi->write(ssbi, addr, buf, len); 256 spin_unlock_irqrestore(&ssbi->lock, flags); 257 258 return ret; 259} 260EXPORT_SYMBOL_GPL(ssbi_write); 261 262static int ssbi_probe(struct platform_device *pdev) 263{ 264 struct device_node *np = pdev->dev.of_node; 265 struct resource *mem_res; 266 struct ssbi *ssbi; 267 const char *type; 268 269 ssbi = devm_kzalloc(&pdev->dev, sizeof(*ssbi), GFP_KERNEL); 270 if (!ssbi) 271 return -ENOMEM; 272 273 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 274 ssbi->base = devm_ioremap_resource(&pdev->dev, mem_res); 275 if (IS_ERR(ssbi->base)) 276 return PTR_ERR(ssbi->base); 277 278 platform_set_drvdata(pdev, ssbi); 279 280 type = of_get_property(np, "qcom,controller-type", NULL); 281 if (type == NULL) { 282 dev_err(&pdev->dev, "Missing qcom,controller-type property\n"); 283 return -EINVAL; 284 } 285 dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type); 286 if (strcmp(type, "ssbi") == 0) 287 ssbi->controller_type = MSM_SBI_CTRL_SSBI; 288 else if (strcmp(type, "ssbi2") == 0) 289 ssbi->controller_type = MSM_SBI_CTRL_SSBI2; 290 else if (strcmp(type, "pmic-arbiter") == 0) 291 ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; 292 else { 293 dev_err(&pdev->dev, "Unknown qcom,controller-type\n"); 294 return -EINVAL; 295 } 296 297 if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { 298 ssbi->read = ssbi_pa_read_bytes; 299 ssbi->write = ssbi_pa_write_bytes; 300 } else { 301 ssbi->read = ssbi_read_bytes; 302 ssbi->write = ssbi_write_bytes; 303 } 304 305 spin_lock_init(&ssbi->lock); 306 307 return devm_of_platform_populate(&pdev->dev); 308} 309 310static const struct of_device_id ssbi_match_table[] = { 311 { .compatible = "qcom,ssbi" }, 312 {} 313}; 314MODULE_DEVICE_TABLE(of, ssbi_match_table); 315 316static struct platform_driver ssbi_driver = { 317 .probe = ssbi_probe, 318 .driver = { 319 .name = "ssbi", 320 .of_match_table = ssbi_match_table, 321 }, 322}; 323module_platform_driver(ssbi_driver); 324 325MODULE_LICENSE("GPL v2"); 326MODULE_VERSION("1.0"); 327MODULE_ALIAS("platform:ssbi"); 328MODULE_AUTHOR("Dima Zavin <dima@android.com>");