ssbi.c revision 6378c1e511d97218f33936f47888095023c9ffaf
1ce44bf5b5544cbe6358abb01f039361a99b80901David Brown/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. 2e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * Copyright (c) 2010, Google Inc. 3e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * 4e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * Original authors: Code Aurora Forum 5e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * 6e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * Author: Dima Zavin <dima@android.com> 7e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * - Largely rewritten from original to not be an i2c driver. 8e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * 9e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * This program is free software; you can redistribute it and/or modify 10e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * it under the terms of the GNU General Public License version 2 and 11e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * only version 2 as published by the Free Software Foundation. 12e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * 13e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * This program is distributed in the hope that it will be useful, 14e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * but WITHOUT ANY WARRANTY; without even the implied warranty of 15e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke * GNU General Public License for more details. 17e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke */ 18e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 19e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define pr_fmt(fmt) "%s: " fmt, __func__ 20e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 21e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/delay.h> 22e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/err.h> 23e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/io.h> 24e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/kernel.h> 25e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/platform_device.h> 26e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/slab.h> 27ce44bf5b5544cbe6358abb01f039361a99b80901David Brown#include <linux/ssbi.h> 28e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#include <linux/module.h> 2997f00f7120fe3396302693cdc4b1d11bbacad963David Brown#include <linux/of.h> 3097f00f7120fe3396302693cdc4b1d11bbacad963David Brown#include <linux/of_device.h> 31e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 32e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI 2.0 controller registers */ 33e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_CMD 0x0008 34e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_RD 0x0010 35e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_STATUS 0x0014 36e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI2_MODE2 0x001C 37e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 38e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_CMD fields */ 39e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_CMD_RDWRN (1 << 24) 40e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 41e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_STATUS fields */ 42e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_STATUS_RD_READY (1 << 2) 43e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_STATUS_READY (1 << 1) 44e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_STATUS_MCHN_BUSY (1 << 0) 45e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 46e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_MODE2 fields */ 47e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04 48e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT) 49e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 50e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \ 51e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \ 52e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke SSBI_MODE2_REG_ADDR_15_8_MASK)) 53e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 54e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI PMIC Arbiter command registers */ 55e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_CMD 0x0000 56e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_RD_STATUS 0x0004 57e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 58e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_PA_CMD fields */ 59e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_CMD_RDWRN (1 << 24) 60e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/ 61e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 62e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke/* SSBI_PA_RD_STATUS fields */ 63e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27) 64e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26) 65e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 66e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke#define SSBI_TIMEOUT_US 100 67e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 68ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstruct ssbi { 69e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke struct device *slave; 70e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke void __iomem *base; 71e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke spinlock_t lock; 72ce44bf5b5544cbe6358abb01f039361a99b80901David Brown enum ssbi_controller_type controller_type; 73ce44bf5b5544cbe6358abb01f039361a99b80901David Brown int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); 74ce44bf5b5544cbe6358abb01f039361a99b80901David Brown int (*write)(struct ssbi *, u16 addr, u8 *buf, int len); 75e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}; 76e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 77ce44bf5b5544cbe6358abb01f039361a99b80901David Brown#define to_ssbi(dev) platform_get_drvdata(to_platform_device(dev)) 78e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 79ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg) 80e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 81e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return readl(ssbi->base + reg); 82e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 83e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 84ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg) 85e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 86e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke writel(val, ssbi->base + reg); 87e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 88e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 893f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/* 903f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * Via private exchange with one of the original authors, the hardware 913f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * should generally finish a transaction in about 5us. The worst 923f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * case, is when using the arbiter and both other CPUs have just 933f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * started trying to use the SSBI bus will result in a time of about 943f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * 20us. It should never take longer than this. 953f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * 963f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * As such, this wait merely spins, with a udelay. 973f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */ 98ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask) 99e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 100e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 timeout = SSBI_TIMEOUT_US; 101e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 val; 102e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 103e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke while (timeout--) { 104e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke val = ssbi_readl(ssbi, SSBI2_STATUS); 105e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) 106e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return 0; 107e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke udelay(1); 108e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 109e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 110e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return -ETIMEDOUT; 111e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 112e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 113e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int 114ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) 115e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 116e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); 117e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke int ret = 0; 118e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 119e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { 120e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); 121e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); 122e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ssbi_writel(ssbi, mode2, SSBI2_MODE2); 123e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 124e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 125e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke while (len) { 126e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); 127e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ret) 128e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err; 129e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 130e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ssbi_writel(ssbi, cmd, SSBI2_CMD); 131e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0); 132e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ret) 133e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err; 134e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; 135e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke len--; 136e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 137e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 138e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr: 139e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return ret; 140e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 141e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 142e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int 143ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) 144e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 145e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke int ret = 0; 146e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 147e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { 148e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); 149e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); 150e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ssbi_writel(ssbi, mode2, SSBI2_MODE2); 151e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 152e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 153e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke while (len) { 154e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); 155e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ret) 156e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err; 157e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 158e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD); 159e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY); 160e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ret) 161e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err; 162e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke buf++; 163e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke len--; 164e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 165e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 166e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr: 167e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return ret; 168e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 169e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 1703f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown/* 1713f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * See ssbi_wait_mask for an explanation of the time and the 1723f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown * busywait. 1733f7a73b57c2f57aa25342ebdb7312f78c68502ebDavid Brown */ 174e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic inline int 175ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data) 176e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 177e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 timeout = SSBI_TIMEOUT_US; 178e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 rd_status = 0; 179e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 180e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ssbi_writel(ssbi, cmd, SSBI_PA_CMD); 181e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 182e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke while (timeout--) { 183e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS); 184e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 18537799ef4fa95ceec09b5c214fb281c6e6acddf5bDavid Brown if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) 186e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return -EPERM; 187e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 188e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) { 189e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (data) 190e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke *data = rd_status & 0xff; 191e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return 0; 192e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 193e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke udelay(1); 194e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 195e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 196e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return -ETIMEDOUT; 197e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 198e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 199e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int 200ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) 201e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 202e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 cmd; 203e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke int ret = 0; 204e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 205e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8; 206e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 207e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke while (len) { 208ce44bf5b5544cbe6358abb01f039361a99b80901David Brown ret = ssbi_pa_transfer(ssbi, cmd, buf); 209e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ret) 210e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err; 211e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke buf++; 212e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke len--; 213e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 214e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 215e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr: 216e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return ret; 217e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 218e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 219e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkestatic int 220ce44bf5b5544cbe6358abb01f039361a99b80901David Brownssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) 221e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 222e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke u32 cmd; 223e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke int ret = 0; 224e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 225e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke while (len) { 226e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf; 227ce44bf5b5544cbe6358abb01f039361a99b80901David Brown ret = ssbi_pa_transfer(ssbi, cmd, NULL); 228e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ret) 229e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err; 230e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke buf++; 231e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke len--; 232e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 233e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 234e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr: 235e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return ret; 236e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 237e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 238ce44bf5b5544cbe6358abb01f039361a99b80901David Brownint ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) 239e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 240ce44bf5b5544cbe6358abb01f039361a99b80901David Brown struct ssbi *ssbi = to_ssbi(dev); 241e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke unsigned long flags; 242e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke int ret; 243e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 244e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke spin_lock_irqsave(&ssbi->lock, flags); 245e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = ssbi->read(ssbi, addr, buf, len); 246e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke spin_unlock_irqrestore(&ssbi->lock, flags); 247e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 248e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return ret; 249e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 250ce44bf5b5544cbe6358abb01f039361a99b80901David BrownEXPORT_SYMBOL_GPL(ssbi_read); 251e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 252ce44bf5b5544cbe6358abb01f039361a99b80901David Brownint ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) 253e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 254ce44bf5b5544cbe6358abb01f039361a99b80901David Brown struct ssbi *ssbi = to_ssbi(dev); 255e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke unsigned long flags; 256e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke int ret; 257e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 258e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke spin_lock_irqsave(&ssbi->lock, flags); 259e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = ssbi->write(ssbi, addr, buf, len); 260e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke spin_unlock_irqrestore(&ssbi->lock, flags); 261e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 262e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return ret; 263e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 264ce44bf5b5544cbe6358abb01f039361a99b80901David BrownEXPORT_SYMBOL_GPL(ssbi_write); 265e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 266ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_probe(struct platform_device *pdev) 267e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 26897f00f7120fe3396302693cdc4b1d11bbacad963David Brown struct device_node *np = pdev->dev.of_node; 269e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke struct resource *mem_res; 270ce44bf5b5544cbe6358abb01f039361a99b80901David Brown struct ssbi *ssbi; 271e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke int ret = 0; 27297f00f7120fe3396302693cdc4b1d11bbacad963David Brown const char *type; 273e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 274ce44bf5b5544cbe6358abb01f039361a99b80901David Brown ssbi = kzalloc(sizeof(struct ssbi), GFP_KERNEL); 275e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (!ssbi) { 276e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke pr_err("can not allocate ssbi_data\n"); 277e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return -ENOMEM; 278e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 279e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 280e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 281e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (!mem_res) { 282e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke pr_err("missing mem resource\n"); 283e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = -EINVAL; 284e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err_get_mem_res; 285e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 286e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 287e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ssbi->base = ioremap(mem_res->start, resource_size(mem_res)); 288e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (!ssbi->base) { 289e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start); 290e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke ret = -EINVAL; 291e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke goto err_ioremap; 292e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 293e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke platform_set_drvdata(pdev, ssbi); 294e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 29597f00f7120fe3396302693cdc4b1d11bbacad963David Brown type = of_get_property(np, "qcom,controller-type", NULL); 29697f00f7120fe3396302693cdc4b1d11bbacad963David Brown if (type == NULL) { 29797f00f7120fe3396302693cdc4b1d11bbacad963David Brown pr_err("Missing qcom,controller-type property\n"); 29897f00f7120fe3396302693cdc4b1d11bbacad963David Brown ret = -EINVAL; 29997f00f7120fe3396302693cdc4b1d11bbacad963David Brown goto err_ssbi_controller; 30097f00f7120fe3396302693cdc4b1d11bbacad963David Brown } 30197f00f7120fe3396302693cdc4b1d11bbacad963David Brown dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type); 30297f00f7120fe3396302693cdc4b1d11bbacad963David Brown if (strcmp(type, "ssbi") == 0) 30397f00f7120fe3396302693cdc4b1d11bbacad963David Brown ssbi->controller_type = MSM_SBI_CTRL_SSBI; 30497f00f7120fe3396302693cdc4b1d11bbacad963David Brown else if (strcmp(type, "ssbi2") == 0) 30597f00f7120fe3396302693cdc4b1d11bbacad963David Brown ssbi->controller_type = MSM_SBI_CTRL_SSBI2; 30697f00f7120fe3396302693cdc4b1d11bbacad963David Brown else if (strcmp(type, "pmic-arbiter") == 0) 30797f00f7120fe3396302693cdc4b1d11bbacad963David Brown ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; 30897f00f7120fe3396302693cdc4b1d11bbacad963David Brown else { 30997f00f7120fe3396302693cdc4b1d11bbacad963David Brown pr_err("Unknown qcom,controller-type\n"); 31097f00f7120fe3396302693cdc4b1d11bbacad963David Brown ret = -EINVAL; 31197f00f7120fe3396302693cdc4b1d11bbacad963David Brown goto err_ssbi_controller; 31297f00f7120fe3396302693cdc4b1d11bbacad963David Brown } 31397f00f7120fe3396302693cdc4b1d11bbacad963David Brown 314e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { 315ce44bf5b5544cbe6358abb01f039361a99b80901David Brown ssbi->read = ssbi_pa_read_bytes; 316ce44bf5b5544cbe6358abb01f039361a99b80901David Brown ssbi->write = ssbi_pa_write_bytes; 317e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } else { 318ce44bf5b5544cbe6358abb01f039361a99b80901David Brown ssbi->read = ssbi_read_bytes; 319ce44bf5b5544cbe6358abb01f039361a99b80901David Brown ssbi->write = ssbi_write_bytes; 320e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke } 321e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 322e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke spin_lock_init(&ssbi->lock); 323e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 32497f00f7120fe3396302693cdc4b1d11bbacad963David Brown ret = of_platform_populate(np, NULL, NULL, &pdev->dev); 325e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke if (ret) 32697f00f7120fe3396302693cdc4b1d11bbacad963David Brown goto err_ssbi_controller; 327e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 328e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return 0; 329e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 33097f00f7120fe3396302693cdc4b1d11bbacad963David Brownerr_ssbi_controller: 331e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke platform_set_drvdata(pdev, NULL); 332e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke iounmap(ssbi->base); 333e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr_ioremap: 334e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitkeerr_get_mem_res: 335e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke kfree(ssbi); 336e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return ret; 337e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 338e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 339ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int ssbi_remove(struct platform_device *pdev) 340e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 341ce44bf5b5544cbe6358abb01f039361a99b80901David Brown struct ssbi *ssbi = platform_get_drvdata(pdev); 342e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 343e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke platform_set_drvdata(pdev, NULL); 344e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke iounmap(ssbi->base); 345e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke kfree(ssbi); 346e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke return 0; 347e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 348e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 34997f00f7120fe3396302693cdc4b1d11bbacad963David Brownstatic struct of_device_id ssbi_match_table[] = { 35097f00f7120fe3396302693cdc4b1d11bbacad963David Brown { .compatible = "qcom,ssbi" }, 35197f00f7120fe3396302693cdc4b1d11bbacad963David Brown {} 35297f00f7120fe3396302693cdc4b1d11bbacad963David Brown}; 3536378c1e511d97218f33936f47888095023c9ffafStephen BoydMODULE_DEVICE_TABLE(of, ssbi_match_table); 35497f00f7120fe3396302693cdc4b1d11bbacad963David Brown 355ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic struct platform_driver ssbi_driver = { 356ce44bf5b5544cbe6358abb01f039361a99b80901David Brown .probe = ssbi_probe, 357ce44bf5b5544cbe6358abb01f039361a99b80901David Brown .remove = ssbi_remove, 358e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke .driver = { 359ce44bf5b5544cbe6358abb01f039361a99b80901David Brown .name = "ssbi", 360e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke .owner = THIS_MODULE, 36197f00f7120fe3396302693cdc4b1d11bbacad963David Brown .of_match_table = ssbi_match_table, 362e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke }, 363e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke}; 364e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 365ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic int __init ssbi_init(void) 366e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 367ce44bf5b5544cbe6358abb01f039361a99b80901David Brown return platform_driver_register(&ssbi_driver); 368e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 369ce44bf5b5544cbe6358abb01f039361a99b80901David Brownmodule_init(ssbi_init); 370e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 371ce44bf5b5544cbe6358abb01f039361a99b80901David Brownstatic void __exit ssbi_exit(void) 372e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke{ 373ce44bf5b5544cbe6358abb01f039361a99b80901David Brown platform_driver_unregister(&ssbi_driver); 374e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke} 375ce44bf5b5544cbe6358abb01f039361a99b80901David Brownmodule_exit(ssbi_exit) 376e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth Heitke 377e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_LICENSE("GPL v2"); 378e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_VERSION("1.0"); 379ce44bf5b5544cbe6358abb01f039361a99b80901David BrownMODULE_ALIAS("platform:ssbi"); 380e44b0ceee4cc2a926225e73ac1e20b9a5bb22c2dKenneth HeitkeMODULE_AUTHOR("Dima Zavin <dima@android.com>"); 381