Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.2-rc4 451 lines 11 kB view raw
1/* 2 * Copyright (C) ST-Ericsson SA 2010 3 * 4 * License Terms: GNU General Public License v2 5 * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> 6 * 7 * U5500 PRCM Unit interface driver 8 */ 9#include <linux/module.h> 10#include <linux/kernel.h> 11#include <linux/delay.h> 12#include <linux/errno.h> 13#include <linux/err.h> 14#include <linux/spinlock.h> 15#include <linux/io.h> 16#include <linux/slab.h> 17#include <linux/mutex.h> 18#include <linux/completion.h> 19#include <linux/irq.h> 20#include <linux/jiffies.h> 21#include <linux/bitops.h> 22#include <linux/interrupt.h> 23#include <linux/mfd/dbx500-prcmu.h> 24#include <mach/hardware.h> 25#include <mach/irqs.h> 26#include <mach/db5500-regs.h> 27#include "dbx500-prcmu-regs.h" 28 29#define _PRCM_MB_HEADER (tcdm_base + 0xFE8) 30#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0) 31#define PRCM_REQ_MB1_HEADER (_PRCM_MB_HEADER + 0x1) 32#define PRCM_REQ_MB2_HEADER (_PRCM_MB_HEADER + 0x2) 33#define PRCM_REQ_MB3_HEADER (_PRCM_MB_HEADER + 0x3) 34#define PRCM_REQ_MB4_HEADER (_PRCM_MB_HEADER + 0x4) 35#define PRCM_REQ_MB5_HEADER (_PRCM_MB_HEADER + 0x5) 36#define PRCM_REQ_MB6_HEADER (_PRCM_MB_HEADER + 0x6) 37#define PRCM_REQ_MB7_HEADER (_PRCM_MB_HEADER + 0x7) 38#define PRCM_ACK_MB0_HEADER (_PRCM_MB_HEADER + 0x8) 39#define PRCM_ACK_MB1_HEADER (_PRCM_MB_HEADER + 0x9) 40#define PRCM_ACK_MB2_HEADER (_PRCM_MB_HEADER + 0xa) 41#define PRCM_ACK_MB3_HEADER (_PRCM_MB_HEADER + 0xb) 42#define PRCM_ACK_MB4_HEADER (_PRCM_MB_HEADER + 0xc) 43#define PRCM_ACK_MB5_HEADER (_PRCM_MB_HEADER + 0xd) 44#define PRCM_ACK_MB6_HEADER (_PRCM_MB_HEADER + 0xe) 45#define PRCM_ACK_MB7_HEADER (_PRCM_MB_HEADER + 0xf) 46 47/* Req Mailboxes */ 48#define PRCM_REQ_MB0 (tcdm_base + 0xFD8) 49#define PRCM_REQ_MB1 (tcdm_base + 0xFCC) 50#define PRCM_REQ_MB2 (tcdm_base + 0xFC4) 51#define PRCM_REQ_MB3 (tcdm_base + 0xFC0) 52#define PRCM_REQ_MB4 (tcdm_base + 0xF98) 53#define PRCM_REQ_MB5 (tcdm_base + 0xF90) 54#define PRCM_REQ_MB6 (tcdm_base + 0xF8C) 55#define PRCM_REQ_MB7 (tcdm_base + 0xF84) 56 57/* Ack Mailboxes */ 58#define PRCM_ACK_MB0 (tcdm_base + 0xF38) 59#define PRCM_ACK_MB1 (tcdm_base + 0xF30) 60#define PRCM_ACK_MB2 (tcdm_base + 0xF24) 61#define PRCM_ACK_MB3 (tcdm_base + 0xF20) 62#define PRCM_ACK_MB4 (tcdm_base + 0xF1C) 63#define PRCM_ACK_MB5 (tcdm_base + 0xF14) 64#define PRCM_ACK_MB6 (tcdm_base + 0xF0C) 65#define PRCM_ACK_MB7 (tcdm_base + 0xF08) 66 67enum mb_return_code { 68 RC_SUCCESS, 69 RC_FAIL, 70}; 71 72/* Mailbox 0 headers. */ 73enum mb0_header { 74 /* request */ 75 RMB0H_PWR_STATE_TRANS = 1, 76 RMB0H_WAKE_UP_CFG, 77 RMB0H_RD_WAKE_UP_ACK, 78 /* acknowledge */ 79 AMB0H_WAKE_UP = 1, 80}; 81 82/* Mailbox 5 headers. */ 83enum mb5_header { 84 MB5H_I2C_WRITE = 1, 85 MB5H_I2C_READ, 86}; 87 88/* Request mailbox 5 fields. */ 89#define PRCM_REQ_MB5_I2C_SLAVE (PRCM_REQ_MB5 + 0) 90#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 1) 91#define PRCM_REQ_MB5_I2C_SIZE (PRCM_REQ_MB5 + 2) 92#define PRCM_REQ_MB5_I2C_DATA (PRCM_REQ_MB5 + 4) 93 94/* Acknowledge mailbox 5 fields. */ 95#define PRCM_ACK_MB5_RETURN_CODE (PRCM_ACK_MB5 + 0) 96#define PRCM_ACK_MB5_I2C_DATA (PRCM_ACK_MB5 + 4) 97 98#define NUM_MB 8 99#define MBOX_BIT BIT 100#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1) 101 102/* 103* Used by MCDE to setup all necessary PRCMU registers 104*/ 105#define PRCMU_RESET_DSIPLL 0x00004000 106#define PRCMU_UNCLAMP_DSIPLL 0x00400800 107 108/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0x8, = 50 Mhz*/ 109#define PRCMU_DSI_CLOCK_SETTING 0x00000128 110/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */ 111#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135 112#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121 113#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002 114#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201 115#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101 116 117#define PRCMU_ENABLE_PLLDSI 0x00000001 118#define PRCMU_DISABLE_PLLDSI 0x00000000 119 120#define PRCMU_DSI_RESET_SW 0x00000003 121#define PRCMU_RESOUTN0_PIN 0x00000001 122#define PRCMU_RESOUTN1_PIN 0x00000002 123#define PRCMU_RESOUTN2_PIN 0x00000004 124 125#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3 126 127/* 128 * mb0_transfer - state needed for mailbox 0 communication. 129 * @lock: The transaction lock. 130 */ 131static struct { 132 spinlock_t lock; 133} mb0_transfer; 134 135/* 136 * mb5_transfer - state needed for mailbox 5 communication. 137 * @lock: The transaction lock. 138 * @work: The transaction completion structure. 139 * @ack: Reply ("acknowledge") data. 140 */ 141static struct { 142 struct mutex lock; 143 struct completion work; 144 struct { 145 u8 header; 146 u8 status; 147 u8 value[4]; 148 } ack; 149} mb5_transfer; 150 151/* PRCMU TCDM base IO address. */ 152static __iomem void *tcdm_base; 153 154/** 155 * db5500_prcmu_abb_read() - Read register value(s) from the ABB. 156 * @slave: The I2C slave address. 157 * @reg: The (start) register address. 158 * @value: The read out value(s). 159 * @size: The number of registers to read. 160 * 161 * Reads register value(s) from the ABB. 162 * @size has to be <= 4. 163 */ 164int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) 165{ 166 int r; 167 168 if ((size < 1) || (4 < size)) 169 return -EINVAL; 170 171 mutex_lock(&mb5_transfer.lock); 172 173 while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) 174 cpu_relax(); 175 writeb(slave, PRCM_REQ_MB5_I2C_SLAVE); 176 writeb(reg, PRCM_REQ_MB5_I2C_REG); 177 writeb(size, PRCM_REQ_MB5_I2C_SIZE); 178 writeb(MB5H_I2C_READ, PRCM_REQ_MB5_HEADER); 179 180 writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); 181 wait_for_completion(&mb5_transfer.work); 182 183 r = 0; 184 if ((mb5_transfer.ack.header == MB5H_I2C_READ) && 185 (mb5_transfer.ack.status == RC_SUCCESS)) 186 memcpy(value, mb5_transfer.ack.value, (size_t)size); 187 else 188 r = -EIO; 189 190 mutex_unlock(&mb5_transfer.lock); 191 192 return r; 193} 194 195/** 196 * db5500_prcmu_abb_write() - Write register value(s) to the ABB. 197 * @slave: The I2C slave address. 198 * @reg: The (start) register address. 199 * @value: The value(s) to write. 200 * @size: The number of registers to write. 201 * 202 * Writes register value(s) to the ABB. 203 * @size has to be <= 4. 204 */ 205int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) 206{ 207 int r; 208 209 if ((size < 1) || (4 < size)) 210 return -EINVAL; 211 212 mutex_lock(&mb5_transfer.lock); 213 214 while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) 215 cpu_relax(); 216 writeb(slave, PRCM_REQ_MB5_I2C_SLAVE); 217 writeb(reg, PRCM_REQ_MB5_I2C_REG); 218 writeb(size, PRCM_REQ_MB5_I2C_SIZE); 219 memcpy_toio(PRCM_REQ_MB5_I2C_DATA, value, size); 220 writeb(MB5H_I2C_WRITE, PRCM_REQ_MB5_HEADER); 221 222 writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); 223 wait_for_completion(&mb5_transfer.work); 224 225 if ((mb5_transfer.ack.header == MB5H_I2C_WRITE) && 226 (mb5_transfer.ack.status == RC_SUCCESS)) 227 r = 0; 228 else 229 r = -EIO; 230 231 mutex_unlock(&mb5_transfer.lock); 232 233 return r; 234} 235 236int db5500_prcmu_enable_dsipll(void) 237{ 238 int i; 239 240 /* Enable DSIPLL_RESETN resets */ 241 writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR); 242 /* Unclamp DSIPLL in/out */ 243 writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR); 244 /* Set DSI PLL FREQ */ 245 writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ); 246 writel(PRCMU_DSI_PLLOUT_SEL_SETTING, 247 PRCM_DSI_PLLOUT_SEL); 248 /* Enable Escape clocks */ 249 writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); 250 251 /* Start DSI PLL */ 252 writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE); 253 /* Reset DSI PLL */ 254 writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET); 255 for (i = 0; i < 10; i++) { 256 if ((readl(PRCM_PLLDSI_LOCKP) & 257 PRCMU_PLLDSI_LOCKP_LOCKED) == PRCMU_PLLDSI_LOCKP_LOCKED) 258 break; 259 udelay(100); 260 } 261 /* Release DSIPLL_RESETN */ 262 writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET); 263 return 0; 264} 265 266int db5500_prcmu_disable_dsipll(void) 267{ 268 /* Disable dsi pll */ 269 writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE); 270 /* Disable escapeclock */ 271 writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); 272 return 0; 273} 274 275int db5500_prcmu_set_display_clocks(void) 276{ 277 /* HDMI and TVCLK Should be handled somewhere else */ 278 /* PLLDIV=8, PLLSW=2, CLKEN=1 */ 279 writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT); 280 /* PLLDIV=14, PLLSW=2, CLKEN=1 */ 281 writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT); 282 return 0; 283} 284 285static void ack_dbb_wakeup(void) 286{ 287 unsigned long flags; 288 289 spin_lock_irqsave(&mb0_transfer.lock, flags); 290 291 while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) 292 cpu_relax(); 293 294 writeb(RMB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER); 295 writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); 296 297 spin_unlock_irqrestore(&mb0_transfer.lock, flags); 298} 299 300static inline void print_unknown_header_warning(u8 n, u8 header) 301{ 302 pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n", 303 header, n); 304} 305 306static bool read_mailbox_0(void) 307{ 308 bool r; 309 u8 header; 310 311 header = readb(PRCM_ACK_MB0_HEADER); 312 switch (header) { 313 case AMB0H_WAKE_UP: 314 r = true; 315 break; 316 default: 317 print_unknown_header_warning(0, header); 318 r = false; 319 break; 320 } 321 writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR); 322 return r; 323} 324 325static bool read_mailbox_1(void) 326{ 327 writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR); 328 return false; 329} 330 331static bool read_mailbox_2(void) 332{ 333 writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR); 334 return false; 335} 336 337static bool read_mailbox_3(void) 338{ 339 writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR); 340 return false; 341} 342 343static bool read_mailbox_4(void) 344{ 345 writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR); 346 return false; 347} 348 349static bool read_mailbox_5(void) 350{ 351 u8 header; 352 353 header = readb(PRCM_ACK_MB5_HEADER); 354 switch (header) { 355 case MB5H_I2C_READ: 356 memcpy_fromio(mb5_transfer.ack.value, PRCM_ACK_MB5_I2C_DATA, 4); 357 case MB5H_I2C_WRITE: 358 mb5_transfer.ack.header = header; 359 mb5_transfer.ack.status = readb(PRCM_ACK_MB5_RETURN_CODE); 360 complete(&mb5_transfer.work); 361 break; 362 default: 363 print_unknown_header_warning(5, header); 364 break; 365 } 366 writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR); 367 return false; 368} 369 370static bool read_mailbox_6(void) 371{ 372 writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR); 373 return false; 374} 375 376static bool read_mailbox_7(void) 377{ 378 writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR); 379 return false; 380} 381 382static bool (* const read_mailbox[NUM_MB])(void) = { 383 read_mailbox_0, 384 read_mailbox_1, 385 read_mailbox_2, 386 read_mailbox_3, 387 read_mailbox_4, 388 read_mailbox_5, 389 read_mailbox_6, 390 read_mailbox_7 391}; 392 393static irqreturn_t prcmu_irq_handler(int irq, void *data) 394{ 395 u32 bits; 396 u8 n; 397 irqreturn_t r; 398 399 bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS); 400 if (unlikely(!bits)) 401 return IRQ_NONE; 402 403 r = IRQ_HANDLED; 404 for (n = 0; bits; n++) { 405 if (bits & MBOX_BIT(n)) { 406 bits -= MBOX_BIT(n); 407 if (read_mailbox[n]()) 408 r = IRQ_WAKE_THREAD; 409 } 410 } 411 return r; 412} 413 414static irqreturn_t prcmu_irq_thread_fn(int irq, void *data) 415{ 416 ack_dbb_wakeup(); 417 return IRQ_HANDLED; 418} 419 420void __init db5500_prcmu_early_init(void) 421{ 422 tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE); 423 spin_lock_init(&mb0_transfer.lock); 424 mutex_init(&mb5_transfer.lock); 425 init_completion(&mb5_transfer.work); 426} 427 428/** 429 * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic 430 * 431 */ 432int __init db5500_prcmu_init(void) 433{ 434 int r = 0; 435 436 if (ux500_is_svp() || !cpu_is_u5500()) 437 return -ENODEV; 438 439 /* Clean up the mailbox interrupts after pre-kernel code. */ 440 writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); 441 442 r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler, 443 prcmu_irq_thread_fn, 0, "prcmu", NULL); 444 if (r < 0) { 445 pr_err("prcmu: Failed to allocate IRQ_DB5500_PRCMU1.\n"); 446 return -EBUSY; 447 } 448 return 0; 449} 450 451arch_initcall(db5500_prcmu_init);