Skip to content

Commit

Permalink
power: Add power driver
Browse files Browse the repository at this point in the history
Signed-off-by: popcornmix <popcornmix@gmail.com>

BCM270x: power: Change initcall level to subsys

Load ordering of modules are determined by the initcall used.
If it's the same initcall level, makefile ordering decides.
Now that the mailbox driver is being moved, it's no longer
placed before the power driver by the linker.
So use a later initcall level to let the mailbox driver
load first.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>

BCM270x: Move power module

Make the power module available on ARCH_BCM2835 by moving it.
The module turns on USB power making it possible to boot
ARCH_BCM2835 directly with the VC bootloader.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
  • Loading branch information
popcornmix committed Jun 22, 2015
1 parent e9ee97a commit 081ebd4
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/soc/Kconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
menu "SOC (System On Chip) specific Drivers"

source "drivers/soc/bcm2835/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/ti/Kconfig"
Expand Down
1 change: 1 addition & 0 deletions drivers/soc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Makefile for the Linux Kernel SOC specific device drivers.
#

obj-y += bcm2835/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
Expand Down
9 changes: 9 additions & 0 deletions drivers/soc/bcm2835/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#
# BCM2835 Soc drivers
#
config BCM2708_POWER
tristate "BCM2708 legacy power driver"
depends on (ARCH_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835) && BCM2708_MBOX
default y
help
Turns on USB power and provides an API for controlling power.
1 change: 1 addition & 0 deletions drivers/soc/bcm2835/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
obj-$(CONFIG_BCM2708_POWER) += bcm2708-power.o
200 changes: 200 additions & 0 deletions drivers/soc/bcm2835/bcm2708-power.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* linux/arch/arm/mach-bcm2708/power.c
*
* Copyright (C) 2010 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This device provides a shared mechanism for controlling the power to
* VideoCore subsystems.
*/

#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/bug.h>
#include <linux/platform_data/mailbox-bcm2708.h>
#include <soc/bcm2835/power.h>

#define DRIVER_NAME "bcm2708_power"

#define BCM_POWER_MAXCLIENTS 4
#define BCM_POWER_NOCLIENT (1<<31)

/* Some drivers expect there devices to be permanently powered */

#ifdef CONFIG_USB
#define BCM_POWER_ALWAYS_ON (BCM_POWER_USB)
#endif

#if 1
#define DPRINTK printk
#else
#define DPRINTK if (0) printk
#endif

struct state_struct {
uint32_t global_request;
uint32_t client_request[BCM_POWER_MAXCLIENTS];
struct semaphore client_mutex;
struct semaphore mutex;
} g_state;

int bcm_power_open(BCM_POWER_HANDLE_T *handle)
{
BCM_POWER_HANDLE_T i;
int ret = -EBUSY;

down(&g_state.client_mutex);

for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) {
if (g_state.client_request[i] == BCM_POWER_NOCLIENT) {
g_state.client_request[i] = BCM_POWER_NONE;
*handle = i;
ret = 0;
break;
}
}

up(&g_state.client_mutex);

DPRINTK("bcm_power_open() -> %d\n", *handle);

return ret;
}
EXPORT_SYMBOL_GPL(bcm_power_open);

int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request)
{
int rc = 0;

DPRINTK("bcm_power_request(%d, %x)\n", handle, request);

if ((handle < BCM_POWER_MAXCLIENTS) &&
(g_state.client_request[handle] != BCM_POWER_NOCLIENT)) {
if (down_interruptible(&g_state.mutex) != 0) {
DPRINTK("bcm_power_request -> interrupted\n");
return -EINTR;
}

if (request != g_state.client_request[handle]) {
uint32_t others_request = 0;
uint32_t global_request;
BCM_POWER_HANDLE_T i;

for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) {
if (i != handle)
others_request |=
g_state.client_request[i];
}
others_request &= ~BCM_POWER_NOCLIENT;

global_request = request | others_request;
if (global_request != g_state.global_request) {
uint32_t actual;

/* Send a request to VideoCore */
bcm_mailbox_write(MBOX_CHAN_POWER,
global_request << 4);

/* Wait for a response during power-up */
if (global_request & ~g_state.global_request) {
rc = bcm_mailbox_read(MBOX_CHAN_POWER,
&actual);
DPRINTK
("bcm_mailbox_read -> %08x, %d\n",
actual, rc);
actual >>= 4;
} else {
rc = 0;
actual = global_request;
}

if (rc == 0) {
if (actual != global_request) {
printk(KERN_ERR
"%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n",
__func__,
g_state.global_request,
global_request, actual, request, others_request);
/* A failure */
BUG_ON((others_request & actual)
!= others_request);
request &= actual;
rc = -EIO;
}

g_state.global_request = actual;
g_state.client_request[handle] =
request;
}
}
}
up(&g_state.mutex);
} else {
rc = -EINVAL;
}
DPRINTK("bcm_power_request -> %d\n", rc);
return rc;
}
EXPORT_SYMBOL_GPL(bcm_power_request);

int bcm_power_close(BCM_POWER_HANDLE_T handle)
{
int rc;

DPRINTK("bcm_power_close(%d)\n", handle);

rc = bcm_power_request(handle, BCM_POWER_NONE);
if (rc == 0)
g_state.client_request[handle] = BCM_POWER_NOCLIENT;

return rc;
}
EXPORT_SYMBOL_GPL(bcm_power_close);

static int __init bcm_power_init(void)
{
#if defined(BCM_POWER_ALWAYS_ON)
BCM_POWER_HANDLE_T always_on_handle;
#endif
int rc = 0;
int i;

printk(KERN_INFO "bcm_power: Broadcom power driver\n");
bcm_mailbox_write(MBOX_CHAN_POWER, 0);

for (i = 0; i < BCM_POWER_MAXCLIENTS; i++)
g_state.client_request[i] = BCM_POWER_NOCLIENT;

sema_init(&g_state.client_mutex, 1);
sema_init(&g_state.mutex, 1);

g_state.global_request = 0;

#if defined(BCM_POWER_ALWAYS_ON)
if (BCM_POWER_ALWAYS_ON) {
bcm_power_open(&always_on_handle);
bcm_power_request(always_on_handle, BCM_POWER_ALWAYS_ON);
}
#endif

return rc;
}

static void __exit bcm_power_exit(void)
{
bcm_mailbox_write(MBOX_CHAN_POWER, 0);
}

/*
* Load after the mailbox driver is initialized (arch_initcall),
* but before depending drivers (module_init).
*/
subsys_initcall(bcm_power_init);
module_exit(bcm_power_exit);

MODULE_AUTHOR("Phil Elwell");
MODULE_DESCRIPTION("Interface to BCM2708 power management");
MODULE_LICENSE("GPL");
61 changes: 61 additions & 0 deletions include/soc/bcm2835/power.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2010 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This device provides a shared mechanism for controlling the power to
* VideoCore subsystems.
*/

#ifndef _BCM2708_POWER_H
#define _BCM2708_POWER_H

#include <linux/types.h>

/* Use meaningful names on each side */
#ifdef __VIDEOCORE__
#define PREFIX(x) ARM_##x
#else
#define PREFIX(x) BCM_##x
#endif

enum {
PREFIX(POWER_SDCARD_BIT),
PREFIX(POWER_UART_BIT),
PREFIX(POWER_MINIUART_BIT),
PREFIX(POWER_USB_BIT),
PREFIX(POWER_I2C0_BIT),
PREFIX(POWER_I2C1_BIT),
PREFIX(POWER_I2C2_BIT),
PREFIX(POWER_SPI_BIT),
PREFIX(POWER_CCP2TX_BIT),
PREFIX(POWER_DSI_BIT),

PREFIX(POWER_MAX)
};

enum {
PREFIX(POWER_SDCARD) = (1 << PREFIX(POWER_SDCARD_BIT)),
PREFIX(POWER_UART) = (1 << PREFIX(POWER_UART_BIT)),
PREFIX(POWER_MINIUART) = (1 << PREFIX(POWER_MINIUART_BIT)),
PREFIX(POWER_USB) = (1 << PREFIX(POWER_USB_BIT)),
PREFIX(POWER_I2C0) = (1 << PREFIX(POWER_I2C0_BIT)),
PREFIX(POWER_I2C1_MASK) = (1 << PREFIX(POWER_I2C1_BIT)),
PREFIX(POWER_I2C2_MASK) = (1 << PREFIX(POWER_I2C2_BIT)),
PREFIX(POWER_SPI_MASK) = (1 << PREFIX(POWER_SPI_BIT)),
PREFIX(POWER_CCP2TX_MASK) = (1 << PREFIX(POWER_CCP2TX_BIT)),
PREFIX(POWER_DSI) = (1 << PREFIX(POWER_DSI_BIT)),

PREFIX(POWER_MASK) = (1 << PREFIX(POWER_MAX)) - 1,
PREFIX(POWER_NONE) = 0
};

typedef unsigned int BCM_POWER_HANDLE_T;

extern int bcm_power_open(BCM_POWER_HANDLE_T *handle);
extern int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request);
extern int bcm_power_close(BCM_POWER_HANDLE_T handle);

#endif

0 comments on commit 081ebd4

Please sign in to comment.