brcm2708: add support for 3.10 kernel
Signed-off-by: Florian Fainelli <florian@openwrt.org> git-svn-id: svn://svn.openwrt.org/openwrt/trunk@37218 3c298f89-4303-0410-b956-a3cf2f4a3e73master
parent
0030861536
commit
dd45e57a2b
|
@ -0,0 +1,250 @@
|
|||
# CONFIG_AIO is not set
|
||||
CONFIG_ALIGNMENT_TRAP=y
|
||||
# CONFIG_APM_EMULATION is not set
|
||||
CONFIG_ARCH_BCM2708=y
|
||||
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
|
||||
CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
|
||||
CONFIG_ARCH_NR_GPIO=0
|
||||
CONFIG_ARCH_REQUIRE_GPIOLIB=y
|
||||
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
|
||||
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
|
||||
CONFIG_ARM=y
|
||||
CONFIG_ARM_AMBA=y
|
||||
CONFIG_ARM_CPU_SUSPEND=y
|
||||
# CONFIG_ARM_ERRATA_326103 is not set
|
||||
# CONFIG_ARM_ERRATA_364296 is not set
|
||||
CONFIG_ARM_ERRATA_411920=y
|
||||
CONFIG_ARM_L1_CACHE_SHIFT=5
|
||||
CONFIG_ARM_NR_BANKS=8
|
||||
# CONFIG_ARM_SP805_WATCHDOG is not set
|
||||
CONFIG_ARM_THUMB=y
|
||||
CONFIG_ARM_UNWIND=y
|
||||
CONFIG_AVERAGE=y
|
||||
# CONFIG_BACKLIGHT_ADP8860 is not set
|
||||
# CONFIG_BACKLIGHT_ADP8870 is not set
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=m
|
||||
CONFIG_BACKLIGHT_GENERIC=m
|
||||
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||
CONFIG_BCM2708_DMAER=y
|
||||
CONFIG_BCM2708_GPIO=y
|
||||
# CONFIG_BCM2708_NOL2CACHE is not set
|
||||
CONFIG_BCM2708_VCHIQ=y
|
||||
CONFIG_BCM2708_VCMEM=y
|
||||
CONFIG_BCM2708_WDT=y
|
||||
# CONFIG_BLK_DEV_INITRD is not set
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_COUNT=16
|
||||
CONFIG_BLK_DEV_RAM_SIZE=4096
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_BRCM_CHAR_DRIVERS=y
|
||||
# CONFIG_CACHE_L2X0 is not set
|
||||
CONFIG_CLKDEV_LOOKUP=y
|
||||
CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait"
|
||||
CONFIG_CMDLINE_FROM_BOOTLOADER=y
|
||||
CONFIG_CONFIGFS_FS=y
|
||||
CONFIG_CONSOLE_TRANSLATIONS=y
|
||||
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||
CONFIG_CPU_32v6=y
|
||||
CONFIG_CPU_ABRT_EV6=y
|
||||
# CONFIG_CPU_BPREDICT_DISABLE is not set
|
||||
CONFIG_CPU_CACHE_V6=y
|
||||
CONFIG_CPU_CACHE_VIPT=y
|
||||
CONFIG_CPU_COPY_V6=y
|
||||
CONFIG_CPU_CP15=y
|
||||
CONFIG_CPU_CP15_MMU=y
|
||||
CONFIG_CPU_HAS_ASID=y
|
||||
CONFIG_CPU_HAS_PMU=y
|
||||
# CONFIG_CPU_ICACHE_DISABLE is not set
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_CPU_IDLE_GOV_LADDER=y
|
||||
CONFIG_CPU_IDLE_GOV_MENU=y
|
||||
CONFIG_CPU_PABRT_V6=y
|
||||
CONFIG_CPU_PM=y
|
||||
CONFIG_CPU_TLB_V6=y
|
||||
CONFIG_CPU_USE_DOMAINS=y
|
||||
CONFIG_CPU_V6=y
|
||||
CONFIG_CRC16=y
|
||||
CONFIG_DEBUG_BUGVERBOSE=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
# CONFIG_DEBUG_USER is not set
|
||||
CONFIG_DEFAULT_CFQ=y
|
||||
# CONFIG_DEFAULT_DEADLINE is not set
|
||||
CONFIG_DEFAULT_IOSCHED="cfq"
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DNOTIFY=y
|
||||
CONFIG_DUMMY_CONSOLE=y
|
||||
CONFIG_ELF_CORE=y
|
||||
CONFIG_ENABLE_MUST_CHECK=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||
CONFIG_EXT4_FS_SECURITY=y
|
||||
CONFIG_FB=y
|
||||
CONFIG_FB_BCM2708=y
|
||||
CONFIG_FB_CFB_COPYAREA=y
|
||||
CONFIG_FB_CFB_FILLRECT=y
|
||||
CONFIG_FB_CFB_IMAGEBLIT=y
|
||||
# CONFIG_FB_WMT_GE_ROPS is not set
|
||||
CONFIG_FIRMWARE_IN_KERNEL=y
|
||||
# CONFIG_FONTS is not set
|
||||
CONFIG_FONT_8x16=y
|
||||
CONFIG_FONT_8x8=y
|
||||
# CONFIG_FPE_FASTFPE is not set
|
||||
# CONFIG_FPE_NWFPE is not set
|
||||
CONFIG_FRAMEBUFFER_CONSOLE=y
|
||||
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
|
||||
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
|
||||
CONFIG_FREEZER=y
|
||||
CONFIG_FS_MBCACHE=y
|
||||
CONFIG_FS_POSIX_ACL=y
|
||||
CONFIG_GENERIC_ACL=y
|
||||
CONFIG_GENERIC_ATOMIC64=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
|
||||
CONFIG_GENERIC_GPIO=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GPIOLIB=y
|
||||
# CONFIG_GPIO_PL061 is not set
|
||||
CONFIG_GPIO_SYSFS=y
|
||||
# CONFIG_HAMRADIO is not set
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT=y
|
||||
CONFIG_HAVE_AOUT=y
|
||||
CONFIG_HAVE_ARCH_KGDB=y
|
||||
CONFIG_HAVE_ARCH_PFN_VALID=y
|
||||
CONFIG_HAVE_CLK=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_DMA_API_DEBUG=y
|
||||
CONFIG_HAVE_DYNAMIC_FTRACE=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
|
||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
||||
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
||||
CONFIG_HAVE_GENERIC_HARDIRQS=y
|
||||
CONFIG_HAVE_IRQ_WORK=y
|
||||
CONFIG_HAVE_KERNEL_GZIP=y
|
||||
CONFIG_HAVE_KERNEL_LZMA=y
|
||||
CONFIG_HAVE_KERNEL_LZO=y
|
||||
CONFIG_HAVE_KERNEL_XZ=y
|
||||
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
|
||||
CONFIG_HAVE_MEMBLOCK=y
|
||||
CONFIG_HAVE_OPROFILE=y
|
||||
CONFIG_HAVE_PERF_EVENTS=y
|
||||
CONFIG_HAVE_PROC_CPU=y
|
||||
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
|
||||
CONFIG_HAVE_SPARSE_IRQ=y
|
||||
CONFIG_HW_CONSOLE=y
|
||||
CONFIG_IKCONFIG=y
|
||||
CONFIG_IKCONFIG_PROC=y
|
||||
CONFIG_INPUT=y
|
||||
CONFIG_INPUT_MOUSEDEV=y
|
||||
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
|
||||
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
|
||||
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
|
||||
CONFIG_IOSCHED_CFQ=y
|
||||
CONFIG_JBD2=y
|
||||
CONFIG_KERNEL_GZIP=y
|
||||
# CONFIG_KERNEL_XZ is not set
|
||||
CONFIG_KTIME_SCALAR=y
|
||||
# CONFIG_LCD_AMS369FG06 is not set
|
||||
CONFIG_LCD_CLASS_DEVICE=m
|
||||
# CONFIG_LCD_L4F00242T03 is not set
|
||||
# CONFIG_LCD_LD9040 is not set
|
||||
# CONFIG_LCD_LMS283GF05 is not set
|
||||
# CONFIG_LCD_LTV350QV is not set
|
||||
# CONFIG_LCD_PLATFORM is not set
|
||||
# CONFIG_LCD_S6E63M0 is not set
|
||||
# CONFIG_LCD_TDO24M is not set
|
||||
# CONFIG_LCD_VGG2432A4 is not set
|
||||
CONFIG_LEDS_GPIO=y
|
||||
# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
|
||||
# CONFIG_LEDS_TRIGGER_TIMER is not set
|
||||
CONFIG_LOGO=y
|
||||
CONFIG_LOGO_LINUX_CLUT224=y
|
||||
# CONFIG_LOGO_LINUX_MONO is not set
|
||||
# CONFIG_LOGO_LINUX_VGA16 is not set
|
||||
CONFIG_MACH_BCM2708=y
|
||||
CONFIG_MAC_PARTITION=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_MAX_RAW_DEVS=256
|
||||
# CONFIG_MFD_T7L66XB is not set
|
||||
CONFIG_MMC=y
|
||||
# CONFIG_MMC_BCM2708 is not set
|
||||
CONFIG_MMC_BLOCK=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_BCM2708=y
|
||||
CONFIG_MMC_SDHCI_BCM2708_DMA=y
|
||||
CONFIG_MMC_SDHCI_IO_ACCESSORS=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
# CONFIG_MTD is not set
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NEED_MACH_MEMORY_H=y
|
||||
CONFIG_NEED_PER_CPU_KM=y
|
||||
CONFIG_NLS=y
|
||||
CONFIG_NLS_ASCII=y
|
||||
CONFIG_NLS_DEFAULT="utf8"
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_OABI_COMPAT=y
|
||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
# CONFIG_PCI_SYSCALL is not set
|
||||
# CONFIG_PDA_POWER is not set
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PM=y
|
||||
CONFIG_PM_CLK=y
|
||||
# CONFIG_PM_DEBUG is not set
|
||||
CONFIG_PM_SLEEP=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
# CONFIG_PREEMPT_RCU is not set
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_PROC_PAGE_MONITOR=y
|
||||
CONFIG_RAW_DRIVER=y
|
||||
CONFIG_SCSI=y
|
||||
# CONFIG_SCSI_LOWLEVEL is not set
|
||||
# CONFIG_SCSI_PROC_FS is not set
|
||||
# CONFIG_SERIAL_8250 is not set
|
||||
# CONFIG_SERIAL_AMBA_PL010 is not set
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
# CONFIG_SQUASHFS is not set
|
||||
# CONFIG_STAGING is not set
|
||||
# CONFIG_STRIP_ASM_SYMS is not set
|
||||
CONFIG_SUSPEND=y
|
||||
CONFIG_SUSPEND_FREEZER=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
# CONFIG_TEXTSEARCH is not set
|
||||
CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_UEVENT_HELPER_PATH=""
|
||||
# CONFIG_UID16 is not set
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
|
||||
# CONFIG_USB_ARCH_HAS_EHCI is not set
|
||||
# CONFIG_USB_ARCH_HAS_OHCI is not set
|
||||
# CONFIG_USB_ARCH_HAS_XHCI is not set
|
||||
CONFIG_USB_COMMON=y
|
||||
# CONFIG_USB_DEVICEFS is not set
|
||||
CONFIG_USB_DEVICE_CLASS=y
|
||||
CONFIG_USB_DWCOTG=y
|
||||
CONFIG_USB_LIBUSUAL=y
|
||||
CONFIG_USB_NET_SMSC95XX=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USB_UAS=y
|
||||
CONFIG_USB_USBNET=y
|
||||
CONFIG_VECTORS_BASE=0xffff0000
|
||||
CONFIG_VFP=y
|
||||
CONFIG_VT=y
|
||||
CONFIG_VT_CONSOLE=y
|
||||
CONFIG_VT_CONSOLE_SLEEP=y
|
||||
CONFIG_VT_HW_CONSOLE_BINDING=y
|
||||
CONFIG_XZ_DEC_ARM=y
|
||||
CONFIG_XZ_DEC_BCJ=y
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZONE_DMA_FLAG=0
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,416 @@
|
|||
diff -urN linux-3.10/drivers/watchdog/bcm2708_wdog.c linux-rpi-3.10.y/drivers/watchdog/bcm2708_wdog.c
|
||||
--- linux-3.10/drivers/watchdog/bcm2708_wdog.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/watchdog/bcm2708_wdog.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -0,0 +1,385 @@
|
||||
+/*
|
||||
+ * Broadcom BCM2708 watchdog driver.
|
||||
+ *
|
||||
+ * (c) Copyright 2010 Broadcom Europe Ltd
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version
|
||||
+ * 2 of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * BCM2708 watchdog driver. Loosely based on wdt driver.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/moduleparam.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/miscdevice.h>
|
||||
+#include <linux/watchdog.h>
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/ioport.h>
|
||||
+#include <linux/notifier.h>
|
||||
+#include <linux/reboot.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <mach/platform.h>
|
||||
+
|
||||
+#include <asm/system.h>
|
||||
+
|
||||
+#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
|
||||
+#define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
|
||||
+
|
||||
+static unsigned long wdog_is_open;
|
||||
+static uint32_t wdog_ticks; /* Ticks to load into wdog timer */
|
||||
+static char expect_close;
|
||||
+
|
||||
+/*
|
||||
+ * Module parameters
|
||||
+ */
|
||||
+
|
||||
+#define WD_TIMO 10 /* Default heartbeat = 60 seconds */
|
||||
+static int heartbeat = WD_TIMO; /* Heartbeat in seconds */
|
||||
+
|
||||
+module_param(heartbeat, int, 0);
|
||||
+MODULE_PARM_DESC(heartbeat,
|
||||
+ "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
|
||||
+ __MODULE_STRING(WD_TIMO) ")");
|
||||
+
|
||||
+static int nowayout = WATCHDOG_NOWAYOUT;
|
||||
+module_param(nowayout, int, 0);
|
||||
+MODULE_PARM_DESC(nowayout,
|
||||
+ "Watchdog cannot be stopped once started (default="
|
||||
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
+
|
||||
+static DEFINE_SPINLOCK(wdog_lock);
|
||||
+
|
||||
+/**
|
||||
+ * Start the watchdog driver.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_start(unsigned long timeout)
|
||||
+{
|
||||
+ uint32_t cur;
|
||||
+ unsigned long flags;
|
||||
+ spin_lock_irqsave(&wdog_lock, flags);
|
||||
+
|
||||
+ /* enable the watchdog */
|
||||
+ iowrite32(PM_PASSWORD | (timeout & PM_WDOG_TIME_SET),
|
||||
+ __io_address(PM_WDOG));
|
||||
+ cur = ioread32(__io_address(PM_RSTC));
|
||||
+ iowrite32(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
|
||||
+ PM_RSTC_WRCFG_FULL_RESET, __io_address(PM_RSTC));
|
||||
+
|
||||
+ spin_unlock_irqrestore(&wdog_lock, flags);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Stop the watchdog driver.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_stop(void)
|
||||
+{
|
||||
+ iowrite32(PM_PASSWORD | PM_RSTC_RESET, __io_address(PM_RSTC));
|
||||
+ printk(KERN_INFO "watchdog stopped\n");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Reload counter one with the watchdog heartbeat. We don't bother
|
||||
+ * reloading the cascade counter.
|
||||
+ */
|
||||
+
|
||||
+static void wdog_ping(void)
|
||||
+{
|
||||
+ wdog_start(wdog_ticks);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @t: the new heartbeat value that needs to be set.
|
||||
+ *
|
||||
+ * Set a new heartbeat value for the watchdog device. If the heartbeat
|
||||
+ * value is incorrect we keep the old value and return -EINVAL. If
|
||||
+ * successful we return 0.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_set_heartbeat(int t)
|
||||
+{
|
||||
+ if (t < 1 || t > WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ heartbeat = t;
|
||||
+ wdog_ticks = SECS_TO_WDOG_TICKS(t);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @file: file handle to the watchdog
|
||||
+ * @buf: buffer to write (unused as data does not matter here
|
||||
+ * @count: count of bytes
|
||||
+ * @ppos: pointer to the position to write. No seeks allowed
|
||||
+ *
|
||||
+ * A write to a watchdog device is defined as a keepalive signal.
|
||||
+ *
|
||||
+ * if 'nowayout' is set then normally a close() is ignored. But
|
||||
+ * if you write 'V' first then the close() will stop the timer.
|
||||
+ */
|
||||
+
|
||||
+static ssize_t wdog_write(struct file *file, const char __user *buf,
|
||||
+ size_t count, loff_t *ppos)
|
||||
+{
|
||||
+ if (count) {
|
||||
+ if (!nowayout) {
|
||||
+ size_t i;
|
||||
+
|
||||
+ /* In case it was set long ago */
|
||||
+ expect_close = 0;
|
||||
+
|
||||
+ for (i = 0; i != count; i++) {
|
||||
+ char c;
|
||||
+ if (get_user(c, buf + i))
|
||||
+ return -EFAULT;
|
||||
+ if (c == 'V')
|
||||
+ expect_close = 42;
|
||||
+ }
|
||||
+ }
|
||||
+ wdog_ping();
|
||||
+ }
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+static int wdog_get_status(void)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+ int status = 0;
|
||||
+ spin_lock_irqsave(&wdog_lock, flags);
|
||||
+ /* FIXME: readback reset reason */
|
||||
+ spin_unlock_irqrestore(&wdog_lock, flags);
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+static uint32_t wdog_get_remaining(void)
|
||||
+{
|
||||
+ uint32_t ret = ioread32(__io_address(PM_WDOG));
|
||||
+ return ret & PM_WDOG_TIME_SET;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @file: file handle to the device
|
||||
+ * @cmd: watchdog command
|
||||
+ * @arg: argument pointer
|
||||
+ *
|
||||
+ * The watchdog API defines a common set of functions for all watchdogs
|
||||
+ * according to their available features. We only actually usefully support
|
||||
+ * querying capabilities and current status.
|
||||
+ */
|
||||
+
|
||||
+static long wdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
+{
|
||||
+ void __user *argp = (void __user *)arg;
|
||||
+ int __user *p = argp;
|
||||
+ int new_heartbeat;
|
||||
+ int status;
|
||||
+ int options;
|
||||
+ uint32_t remaining;
|
||||
+
|
||||
+ struct watchdog_info ident = {
|
||||
+ .options = WDIOF_SETTIMEOUT|
|
||||
+ WDIOF_MAGICCLOSE|
|
||||
+ WDIOF_KEEPALIVEPING,
|
||||
+ .firmware_version = 1,
|
||||
+ .identity = "BCM2708",
|
||||
+ };
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case WDIOC_GETSUPPORT:
|
||||
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
|
||||
+ case WDIOC_GETSTATUS:
|
||||
+ status = wdog_get_status();
|
||||
+ return put_user(status, p);
|
||||
+ case WDIOC_GETBOOTSTATUS:
|
||||
+ return put_user(0, p);
|
||||
+ case WDIOC_KEEPALIVE:
|
||||
+ wdog_ping();
|
||||
+ return 0;
|
||||
+ case WDIOC_SETTIMEOUT:
|
||||
+ if (get_user(new_heartbeat, p))
|
||||
+ return -EFAULT;
|
||||
+ if (wdog_set_heartbeat(new_heartbeat))
|
||||
+ return -EINVAL;
|
||||
+ wdog_ping();
|
||||
+ /* Fall */
|
||||
+ case WDIOC_GETTIMEOUT:
|
||||
+ return put_user(heartbeat, p);
|
||||
+ case WDIOC_GETTIMELEFT:
|
||||
+ remaining = WDOG_TICKS_TO_SECS(wdog_get_remaining());
|
||||
+ return put_user(remaining, p);
|
||||
+ case WDIOC_SETOPTIONS:
|
||||
+ if (get_user(options, p))
|
||||
+ return -EFAULT;
|
||||
+ if (options & WDIOS_DISABLECARD)
|
||||
+ wdog_stop();
|
||||
+ if (options & WDIOS_ENABLECARD)
|
||||
+ wdog_start(wdog_ticks);
|
||||
+ return 0;
|
||||
+ default:
|
||||
+ return -ENOTTY;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @inode: inode of device
|
||||
+ * @file: file handle to device
|
||||
+ *
|
||||
+ * The watchdog device has been opened. The watchdog device is single
|
||||
+ * open and on opening we load the counters.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ if (test_and_set_bit(0, &wdog_is_open))
|
||||
+ return -EBUSY;
|
||||
+ /*
|
||||
+ * Activate
|
||||
+ */
|
||||
+ wdog_start(wdog_ticks);
|
||||
+ return nonseekable_open(inode, file);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @inode: inode to board
|
||||
+ * @file: file handle to board
|
||||
+ *
|
||||
+ * The watchdog has a configurable API. There is a religious dispute
|
||||
+ * between people who want their watchdog to be able to shut down and
|
||||
+ * those who want to be sure if the watchdog manager dies the machine
|
||||
+ * reboots. In the former case we disable the counters, in the latter
|
||||
+ * case you have to open it again very soon.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ if (expect_close == 42) {
|
||||
+ wdog_stop();
|
||||
+ } else {
|
||||
+ printk(KERN_CRIT
|
||||
+ "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
|
||||
+ wdog_ping();
|
||||
+ }
|
||||
+ clear_bit(0, &wdog_is_open);
|
||||
+ expect_close = 0;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @this: our notifier block
|
||||
+ * @code: the event being reported
|
||||
+ * @unused: unused
|
||||
+ *
|
||||
+ * Our notifier is called on system shutdowns. Turn the watchdog
|
||||
+ * off so that it does not fire during the next reboot.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_notify_sys(struct notifier_block *this, unsigned long code,
|
||||
+ void *unused)
|
||||
+{
|
||||
+ if (code == SYS_DOWN || code == SYS_HALT)
|
||||
+ wdog_stop();
|
||||
+ return NOTIFY_DONE;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Kernel Interfaces
|
||||
+ */
|
||||
+
|
||||
+
|
||||
+static const struct file_operations wdog_fops = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .llseek = no_llseek,
|
||||
+ .write = wdog_write,
|
||||
+ .unlocked_ioctl = wdog_ioctl,
|
||||
+ .open = wdog_open,
|
||||
+ .release = wdog_release,
|
||||
+};
|
||||
+
|
||||
+static struct miscdevice wdog_miscdev = {
|
||||
+ .minor = WATCHDOG_MINOR,
|
||||
+ .name = "watchdog",
|
||||
+ .fops = &wdog_fops,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * The WDT card needs to learn about soft shutdowns in order to
|
||||
+ * turn the timebomb registers off.
|
||||
+ */
|
||||
+
|
||||
+static struct notifier_block wdog_notifier = {
|
||||
+ .notifier_call = wdog_notify_sys,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * cleanup_module:
|
||||
+ *
|
||||
+ * Unload the watchdog. You cannot do this with any file handles open.
|
||||
+ * If your watchdog is set to continue ticking on close and you unload
|
||||
+ * it, well it keeps ticking. We won't get the interrupt but the board
|
||||
+ * will not touch PC memory so all is fine. You just have to load a new
|
||||
+ * module in 60 seconds or reboot.
|
||||
+ */
|
||||
+
|
||||
+static void __exit wdog_exit(void)
|
||||
+{
|
||||
+ misc_deregister(&wdog_miscdev);
|
||||
+ unregister_reboot_notifier(&wdog_notifier);
|
||||
+}
|
||||
+
|
||||
+static int __init wdog_init(void)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Check that the heartbeat value is within it's range;
|
||||
+ if not reset to the default */
|
||||
+ if (wdog_set_heartbeat(heartbeat)) {
|
||||
+ wdog_set_heartbeat(WD_TIMO);
|
||||
+ printk(KERN_INFO "bcm2708_wdog: heartbeat value must be "
|
||||
+ "0 < heartbeat < %d, using %d\n",
|
||||
+ WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
|
||||
+ WD_TIMO);
|
||||
+ }
|
||||
+
|
||||
+ ret = register_reboot_notifier(&wdog_notifier);
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR
|
||||
+ "wdt: cannot register reboot notifier (err=%d)\n", ret);
|
||||
+ goto out_reboot;
|
||||
+ }
|
||||
+
|
||||
+ ret = misc_register(&wdog_miscdev);
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR
|
||||
+ "wdt: cannot register miscdev on minor=%d (err=%d)\n",
|
||||
+ WATCHDOG_MINOR, ret);
|
||||
+ goto out_misc;
|
||||
+ }
|
||||
+
|
||||
+ printk(KERN_INFO "bcm2708 watchdog, heartbeat=%d sec (nowayout=%d)\n",
|
||||
+ heartbeat, nowayout);
|
||||
+ return 0;
|
||||
+
|
||||
+out_misc:
|
||||
+ unregister_reboot_notifier(&wdog_notifier);
|
||||
+out_reboot:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+module_init(wdog_init);
|
||||
+module_exit(wdog_exit);
|
||||
+
|
||||
+MODULE_AUTHOR("Luke Diamand");
|
||||
+MODULE_DESCRIPTION("Driver for BCM2708 watchdog");
|
||||
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
diff -urN linux-3.10/drivers/watchdog/Kconfig linux-rpi-3.10.y/drivers/watchdog/Kconfig
|
||||
--- linux-3.10/drivers/watchdog/Kconfig 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/watchdog/Kconfig 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -391,6 +391,12 @@
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called retu_wdt.
|
||||
|
||||
+config BCM2708_WDT
|
||||
+ tristate "BCM2708 Watchdog"
|
||||
+ depends on ARCH_BCM2708
|
||||
+ help
|
||||
+ Enables BCM2708 watchdog support.
|
||||
+
|
||||
# AVR32 Architecture
|
||||
|
||||
config AT32AP700X_WDT
|
||||
diff -urN linux-3.10/drivers/watchdog/Makefile linux-rpi-3.10.y/drivers/watchdog/Makefile
|
||||
--- linux-3.10/drivers/watchdog/Makefile 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/watchdog/Makefile 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -54,6 +54,7 @@
|
||||
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
|
||||
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
|
||||
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
|
||||
+obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o
|
||||
|
||||
# AVR32 Architecture
|
||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,82 @@
|
|||
diff -urN linux-3.10/drivers/net/usb/smsc95xx.c linux-rpi-3.10.y/drivers/net/usb/smsc95xx.c
|
||||
--- linux-3.10/drivers/net/usb/smsc95xx.c 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/net/usb/smsc95xx.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -61,6 +61,7 @@
|
||||
#define SUSPEND_SUSPEND3 (0x08)
|
||||
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
|
||||
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
|
||||
+#define MAC_ADDR_LEN (6)
|
||||
|
||||
struct smsc95xx_priv {
|
||||
u32 mac_cr;
|
||||
@@ -76,6 +77,10 @@
|
||||
module_param(turbo_mode, bool, 0644);
|
||||
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
|
||||
|
||||
+static char *macaddr = ":";
|
||||
+module_param(macaddr, charp, 0);
|
||||
+MODULE_PARM_DESC(macaddr, "MAC address");
|
||||
+
|
||||
static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
|
||||
u32 *data, int in_pm)
|
||||
{
|
||||
@@ -765,8 +770,59 @@
|
||||
return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
|
||||
}
|
||||
|
||||
+/* Check the macaddr module parameter for a MAC address */
|
||||
+static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac)
|
||||
+{
|
||||
+ int i, j, got_num, num;
|
||||
+ u8 mtbl[MAC_ADDR_LEN];
|
||||
+
|
||||
+ if (macaddr[0] == ':')
|
||||
+ return 0;
|
||||
+
|
||||
+ i = 0;
|
||||
+ j = 0;
|
||||
+ num = 0;
|
||||
+ got_num = 0;
|
||||
+ while (j < MAC_ADDR_LEN) {
|
||||
+ if (macaddr[i] && macaddr[i] != ':') {
|
||||
+ got_num++;
|
||||
+ if ('0' <= macaddr[i] && macaddr[i] <= '9')
|
||||
+ num = num * 16 + macaddr[i] - '0';
|
||||
+ else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
|
||||
+ num = num * 16 + 10 + macaddr[i] - 'A';
|
||||
+ else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
|
||||
+ num = num * 16 + 10 + macaddr[i] - 'a';
|
||||
+ else
|
||||
+ break;
|
||||
+ i++;
|
||||
+ } else if (got_num == 2) {
|
||||
+ mtbl[j++] = (u8) num;
|
||||
+ num = 0;
|
||||
+ got_num = 0;
|
||||
+ i++;
|
||||
+ } else {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (j == MAC_ADDR_LEN) {
|
||||
+ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
|
||||
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
|
||||
+ mtbl[3], mtbl[4], mtbl[5]);
|
||||
+ for (i = 0; i < MAC_ADDR_LEN; i++)
|
||||
+ dev_mac[i] = mtbl[i];
|
||||
+ return 1;
|
||||
+ } else {
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void smsc95xx_init_mac_address(struct usbnet *dev)
|
||||
{
|
||||
+ /* Check module parameters */
|
||||
+ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr))
|
||||
+ return;
|
||||
+
|
||||
/* try reading mac address from EEPROM */
|
||||
if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
|
||||
dev->net->dev_addr) == 0) {
|
|
@ -0,0 +1,148 @@
|
|||
diff -urN linux-3.10/drivers/char/hw_random/bcm2708-rng.c linux-rpi-3.10.y/drivers/char/hw_random/bcm2708-rng.c
|
||||
--- linux-3.10/drivers/char/hw_random/bcm2708-rng.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/char/hw_random/bcm2708-rng.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -0,0 +1,117 @@
|
||||
+/**
|
||||
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
|
||||
+ *
|
||||
+ * Redistribution and use in source and binary forms, with or without
|
||||
+ * modification, are permitted provided that the following conditions
|
||||
+ * are met:
|
||||
+ * 1. Redistributions of source code must retain the above copyright
|
||||
+ * notice, this list of conditions, and the following disclaimer,
|
||||
+ * without modification.
|
||||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||||
+ * notice, this list of conditions and the following disclaimer in the
|
||||
+ * documentation and/or other materials provided with the distribution.
|
||||
+ * 3. The names of the above-listed copyright holders may not be used
|
||||
+ * to endorse or promote products derived from this software without
|
||||
+ * specific prior written permission.
|
||||
+ *
|
||||
+ * ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
+ * GNU General Public License ("GPL") version 2, as published by the Free
|
||||
+ * Software Foundation.
|
||||
+ *
|
||||
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/hw_random.h>
|
||||
+#include <linux/printk.h>
|
||||
+
|
||||
+#include <asm/io.h>
|
||||
+#include <mach/hardware.h>
|
||||
+#include <mach/platform.h>
|
||||
+
|
||||
+#define RNG_CTRL (0x0)
|
||||
+#define RNG_STATUS (0x4)
|
||||
+#define RNG_DATA (0x8)
|
||||
+#define RNG_FF_THRESHOLD (0xc)
|
||||
+
|
||||
+/* enable rng */
|
||||
+#define RNG_RBGEN 0x1
|
||||
+/* double speed, less random mode */
|
||||
+#define RNG_RBG2X 0x2
|
||||
+
|
||||
+/* the initial numbers generated are "less random" so will be discarded */
|
||||
+#define RNG_WARMUP_COUNT 0x40000
|
||||
+
|
||||
+static int bcm2708_rng_data_read(struct hwrng *rng, u32 *buffer)
|
||||
+{
|
||||
+ void __iomem *rng_base = (void __iomem *)rng->priv;
|
||||
+ unsigned words;
|
||||
+ /* wait for a random number to be in fifo */
|
||||
+ do {
|
||||
+ words = __raw_readl(rng_base + RNG_STATUS)>>24;
|
||||
+ }
|
||||
+ while (words == 0);
|
||||
+ /* read the random number */
|
||||
+ *buffer = __raw_readl(rng_base + RNG_DATA);
|
||||
+ return 4;
|
||||
+}
|
||||
+
|
||||
+static struct hwrng bcm2708_rng_ops = {
|
||||
+ .name = "bcm2708",
|
||||
+ .data_read = bcm2708_rng_data_read,
|
||||
+};
|
||||
+
|
||||
+static int __init bcm2708_rng_init(void)
|
||||
+{
|
||||
+ void __iomem *rng_base;
|
||||
+ int err;
|
||||
+
|
||||
+ /* map peripheral */
|
||||
+ rng_base = ioremap(RNG_BASE, 0x10);
|
||||
+ pr_info("bcm2708_rng_init=%p\n", rng_base);
|
||||
+ if (!rng_base) {
|
||||
+ pr_err("bcm2708_rng_init failed to ioremap\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ bcm2708_rng_ops.priv = (unsigned long)rng_base;
|
||||
+ /* register driver */
|
||||
+ err = hwrng_register(&bcm2708_rng_ops);
|
||||
+ if (err) {
|
||||
+ pr_err("bcm2708_rng_init hwrng_register()=%d\n", err);
|
||||
+ iounmap(rng_base);
|
||||
+ } else {
|
||||
+ /* set warm-up count & enable */
|
||||
+ __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS);
|
||||
+ __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL);
|
||||
+ }
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static void __exit bcm2708_rng_exit(void)
|
||||
+{
|
||||
+ void __iomem *rng_base = (void __iomem *)bcm2708_rng_ops.priv;
|
||||
+ pr_info("bcm2708_rng_exit\n");
|
||||
+ /* disable rng hardware */
|
||||
+ __raw_writel(0, rng_base + RNG_CTRL);
|
||||
+ /* unregister driver */
|
||||
+ hwrng_unregister(&bcm2708_rng_ops);
|
||||
+ iounmap(rng_base);
|
||||
+}
|
||||
+
|
||||
+module_init(bcm2708_rng_init);
|
||||
+module_exit(bcm2708_rng_exit);
|
||||
+
|
||||
+MODULE_DESCRIPTION("BCM2708 H/W Random Number Generator (RNG) driver");
|
||||
+MODULE_LICENSE("GPL and additional rights");
|
||||
diff -urN linux-3.10/drivers/char/hw_random/Kconfig linux-rpi-3.10.y/drivers/char/hw_random/Kconfig
|
||||
--- linux-3.10/drivers/char/hw_random/Kconfig 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/char/hw_random/Kconfig 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -314,3 +314,15 @@
|
||||
module will be called tpm-rng.
|
||||
|
||||
If unsure, say Y.
|
||||
+
|
||||
+config HW_RANDOM_BCM2708
|
||||
+ tristate "BCM2708 generic true random number generator support"
|
||||
+ depends on HW_RANDOM && ARCH_BCM2708
|
||||
+ ---help---
|
||||
+ This driver provides the kernel-side support for the BCM2708 hardware.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the
|
||||
+ module will be called bcm2708-rng.
|
||||
+
|
||||
+ If unsure, say N.
|
||||
+
|
||||
diff -urN linux-3.10/drivers/char/hw_random/Makefile linux-rpi-3.10.y/drivers/char/hw_random/Makefile
|
||||
--- linux-3.10/drivers/char/hw_random/Makefile 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/char/hw_random/Makefile 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -27,3 +27,4 @@
|
||||
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
|
||||
+obj-$(CONFIG_HW_RANDOM_BCM2708) += bcm2708-rng.o
|
|
@ -0,0 +1,723 @@
|
|||
diff -urN linux-3.10/drivers/staging/media/lirc/Kconfig linux-rpi-3.10.y/drivers/staging/media/lirc/Kconfig
|
||||
--- linux-3.10/drivers/staging/media/lirc/Kconfig 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/staging/media/lirc/Kconfig 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -38,6 +38,12 @@
|
||||
help
|
||||
Driver for Homebrew Parallel Port Receivers
|
||||
|
||||
+config LIRC_RPI
|
||||
+ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi"
|
||||
+ depends on LIRC
|
||||
+ help
|
||||
+ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi
|
||||
+
|
||||
config LIRC_SASEM
|
||||
tristate "Sasem USB IR Remote"
|
||||
depends on LIRC && USB
|
||||
diff -urN linux-3.10/drivers/staging/media/lirc/lirc_rpi.c linux-rpi-3.10.y/drivers/staging/media/lirc/lirc_rpi.c
|
||||
--- linux-3.10/drivers/staging/media/lirc/lirc_rpi.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/staging/media/lirc/lirc_rpi.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -0,0 +1,692 @@
|
||||
+/*
|
||||
+ * lirc_rpi.c
|
||||
+ *
|
||||
+ * lirc_rpi - Device driver that records pulse- and pause-lengths
|
||||
+ * (space-lengths) (just like the lirc_serial driver does)
|
||||
+ * between GPIO interrupt events on the Raspberry Pi.
|
||||
+ * Lots of code has been taken from the lirc_serial module,
|
||||
+ * so I would like say thanks to the authors.
|
||||
+ *
|
||||
+ * Copyright (C) 2012 Aron Robert Szabo <aron@reon.hu>,
|
||||
+ * Michael Bishop <cleverca22@gmail.com>
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License
|
||||
+ * along with this program; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/time.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/irq.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <media/lirc.h>
|
||||
+#include <media/lirc_dev.h>
|
||||
+#include <linux/gpio.h>
|
||||
+
|
||||
+#define LIRC_DRIVER_NAME "lirc_rpi"
|
||||
+#define RBUF_LEN 256
|
||||
+#define LIRC_TRANSMITTER_LATENCY 256
|
||||
+
|
||||
+#ifndef MAX_UDELAY_MS
|
||||
+#define MAX_UDELAY_US 5000
|
||||
+#else
|
||||
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
|
||||
+#endif
|
||||
+
|
||||
+#define dprintk(fmt, args...) \
|
||||
+ do { \
|
||||
+ if (debug) \
|
||||
+ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
|
||||
+ fmt, ## args); \
|
||||
+ } while (0)
|
||||
+
|
||||
+/* module parameters */
|
||||
+
|
||||
+/* set the default GPIO input pin */
|
||||
+static int gpio_in_pin = 18;
|
||||
+/* set the default GPIO output pin */
|
||||
+static int gpio_out_pin = 17;
|
||||
+/* enable debugging messages */
|
||||
+static int debug;
|
||||
+/* -1 = auto, 0 = active high, 1 = active low */
|
||||
+static int sense = -1;
|
||||
+/* use softcarrier by default */
|
||||
+static int softcarrier = 1;
|
||||
+/* 0 = do not invert output, 1 = invert output */
|
||||
+static int invert = 0;
|
||||
+
|
||||
+struct gpio_chip *gpiochip;
|
||||
+struct irq_chip *irqchip;
|
||||
+struct irq_data *irqdata;
|
||||
+
|
||||
+/* forward declarations */
|
||||
+static long send_pulse(unsigned long length);
|
||||
+static void send_space(long length);
|
||||
+static void lirc_rpi_exit(void);
|
||||
+
|
||||
+int valid_gpio_pins[] = { 0, 1, 4, 8, 7, 9, 10, 11, 14, 15, 17, 18, 21, 22, 23,
|
||||
+ 24, 25 };
|
||||
+
|
||||
+static struct platform_device *lirc_rpi_dev;
|
||||
+static struct timeval lasttv = { 0, 0 };
|
||||
+static struct lirc_buffer rbuf;
|
||||
+static spinlock_t lock;
|
||||
+
|
||||
+/* initialized/set in init_timing_params() */
|
||||
+static unsigned int freq = 38000;
|
||||
+static unsigned int duty_cycle = 50;
|
||||
+static unsigned long period;
|
||||
+static unsigned long pulse_width;
|
||||
+static unsigned long space_width;
|
||||
+
|
||||
+static void safe_udelay(unsigned long usecs)
|
||||
+{
|
||||
+ while (usecs > MAX_UDELAY_US) {
|
||||
+ udelay(MAX_UDELAY_US);
|
||||
+ usecs -= MAX_UDELAY_US;
|
||||
+ }
|
||||
+ udelay(usecs);
|
||||
+}
|
||||
+
|
||||
+static int init_timing_params(unsigned int new_duty_cycle,
|
||||
+ unsigned int new_freq)
|
||||
+{
|
||||
+ /*
|
||||
+ * period, pulse/space width are kept with 8 binary places -
|
||||
+ * IE multiplied by 256.
|
||||
+ */
|
||||
+ if (256 * 1000000L / new_freq * new_duty_cycle / 100 <=
|
||||
+ LIRC_TRANSMITTER_LATENCY)
|
||||
+ return -EINVAL;
|
||||
+ if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
|
||||
+ LIRC_TRANSMITTER_LATENCY)
|
||||
+ return -EINVAL;
|
||||
+ duty_cycle = new_duty_cycle;
|
||||
+ freq = new_freq;
|
||||
+ period = 256 * 1000000L / freq;
|
||||
+ pulse_width = period * duty_cycle / 100;
|
||||
+ space_width = period - pulse_width;
|
||||
+ dprintk("in init_timing_params, freq=%d pulse=%ld, "
|
||||
+ "space=%ld\n", freq, pulse_width, space_width);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static long send_pulse_softcarrier(unsigned long length)
|
||||
+{
|
||||
+ int flag;
|
||||
+ unsigned long actual, target, d;
|
||||
+
|
||||
+ length <<= 8;
|
||||
+
|
||||
+ actual = 0; target = 0; flag = 0;
|
||||
+ while (actual < length) {
|
||||
+ if (flag) {
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+ target += space_width;
|
||||
+ } else {
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, !invert);
|
||||
+ target += pulse_width;
|
||||
+ }
|
||||
+ d = (target - actual -
|
||||
+ LIRC_TRANSMITTER_LATENCY + 128) >> 8;
|
||||
+ /*
|
||||
+ * Note - we've checked in ioctl that the pulse/space
|
||||
+ * widths are big enough so that d is > 0
|
||||
+ */
|
||||
+ udelay(d);
|
||||
+ actual += (d << 8) + LIRC_TRANSMITTER_LATENCY;
|
||||
+ flag = !flag;
|
||||
+ }
|
||||
+ return (actual-length) >> 8;
|
||||
+}
|
||||
+
|
||||
+static long send_pulse(unsigned long length)
|
||||
+{
|
||||
+ if (length <= 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (softcarrier) {
|
||||
+ return send_pulse_softcarrier(length);
|
||||
+ } else {
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, !invert);
|
||||
+ safe_udelay(length);
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void send_space(long length)
|
||||
+{
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+ if (length <= 0)
|
||||
+ return;
|
||||
+ safe_udelay(length);
|
||||
+}
|
||||
+
|
||||
+static void rbwrite(int l)
|
||||
+{
|
||||
+ if (lirc_buffer_full(&rbuf)) {
|
||||
+ /* no new signals will be accepted */
|
||||
+ dprintk("Buffer overrun\n");
|
||||
+ return;
|
||||
+ }
|
||||
+ lirc_buffer_write(&rbuf, (void *)&l);
|
||||
+}
|
||||
+
|
||||
+static void frbwrite(int l)
|
||||
+{
|
||||
+ /* simple noise filter */
|
||||
+ static int pulse, space;
|
||||
+ static unsigned int ptr;
|
||||
+
|
||||
+ if (ptr > 0 && (l & PULSE_BIT)) {
|
||||
+ pulse += l & PULSE_MASK;
|
||||
+ if (pulse > 250) {
|
||||
+ rbwrite(space);
|
||||
+ rbwrite(pulse | PULSE_BIT);
|
||||
+ ptr = 0;
|
||||
+ pulse = 0;
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+ if (!(l & PULSE_BIT)) {
|
||||
+ if (ptr == 0) {
|
||||
+ if (l > 20000) {
|
||||
+ space = l;
|
||||
+ ptr++;
|
||||
+ return;
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (l > 20000) {
|
||||
+ space += pulse;
|
||||
+ if (space > PULSE_MASK)
|
||||
+ space = PULSE_MASK;
|
||||
+ space += l;
|
||||
+ if (space > PULSE_MASK)
|
||||
+ space = PULSE_MASK;
|
||||
+ pulse = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+ rbwrite(space);
|
||||
+ rbwrite(pulse | PULSE_BIT);
|
||||
+ ptr = 0;
|
||||
+ pulse = 0;
|
||||
+ }
|
||||
+ }
|
||||
+ rbwrite(l);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs)
|
||||
+{
|
||||
+ struct timeval tv;
|
||||
+ long deltv;
|
||||
+ int data;
|
||||
+ int signal;
|
||||
+
|
||||
+ /* use the GPIO signal level */
|
||||
+ signal = gpiochip->get(gpiochip, gpio_in_pin);
|
||||
+
|
||||
+ /* unmask the irq */
|
||||
+ irqchip->irq_unmask(irqdata);
|
||||
+
|
||||
+ if (sense != -1) {
|
||||
+ /* get current time */
|
||||
+ do_gettimeofday(&tv);
|
||||
+
|
||||
+ /* calc time since last interrupt in microseconds */
|
||||
+ deltv = tv.tv_sec-lasttv.tv_sec;
|
||||
+ if (tv.tv_sec < lasttv.tv_sec ||
|
||||
+ (tv.tv_sec == lasttv.tv_sec &&
|
||||
+ tv.tv_usec < lasttv.tv_usec)) {
|
||||
+ printk(KERN_WARNING LIRC_DRIVER_NAME
|
||||
+ ": AIEEEE: your clock just jumped backwards\n");
|
||||
+ printk(KERN_WARNING LIRC_DRIVER_NAME
|
||||
+ ": %d %d %lx %lx %lx %lx\n", signal, sense,
|
||||
+ tv.tv_sec, lasttv.tv_sec,
|
||||
+ tv.tv_usec, lasttv.tv_usec);
|
||||
+ data = PULSE_MASK;
|
||||
+ } else if (deltv > 15) {
|
||||
+ data = PULSE_MASK; /* really long time */
|
||||
+ if (!(signal^sense)) {
|
||||
+ /* sanity check */
|
||||
+ printk(KERN_WARNING LIRC_DRIVER_NAME
|
||||
+ ": AIEEEE: %d %d %lx %lx %lx %lx\n",
|
||||
+ signal, sense, tv.tv_sec, lasttv.tv_sec,
|
||||
+ tv.tv_usec, lasttv.tv_usec);
|
||||
+ /*
|
||||
+ * detecting pulse while this
|
||||
+ * MUST be a space!
|
||||
+ */
|
||||
+ sense = sense ? 0 : 1;
|
||||
+ }
|
||||
+ } else {
|
||||
+ data = (int) (deltv*1000000 +
|
||||
+ (tv.tv_usec - lasttv.tv_usec));
|
||||
+ }
|
||||
+ frbwrite(signal^sense ? data : (data|PULSE_BIT));
|
||||
+ lasttv = tv;
|
||||
+ wake_up_interruptible(&rbuf.wait_poll);
|
||||
+ }
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static int is_right_chip(struct gpio_chip *chip, void *data)
|
||||
+{
|
||||
+ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label));
|
||||
+
|
||||
+ if (strcmp(data, chip->label) == 0)
|
||||
+ return 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int init_port(void)
|
||||
+{
|
||||
+ int i, nlow, nhigh, ret, irq;
|
||||
+
|
||||
+ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
|
||||
+
|
||||
+ if (!gpiochip)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) {
|
||||
+ printk(KERN_ALERT LIRC_DRIVER_NAME
|
||||
+ ": cant claim gpio pin %d\n", gpio_out_pin);
|
||||
+ ret = -ENODEV;
|
||||
+ goto exit_init_port;
|
||||
+ }
|
||||
+
|
||||
+ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) {
|
||||
+ printk(KERN_ALERT LIRC_DRIVER_NAME
|
||||
+ ": cant claim gpio pin %d\n", gpio_in_pin);
|
||||
+ ret = -ENODEV;
|
||||
+ goto exit_gpio_free_out_pin;
|
||||
+ }
|
||||
+
|
||||
+ gpiochip->direction_input(gpiochip, gpio_in_pin);
|
||||
+ gpiochip->direction_output(gpiochip, gpio_out_pin, 1);
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+
|
||||
+ irq = gpiochip->to_irq(gpiochip, gpio_in_pin);
|
||||
+ dprintk("to_irq %d\n", irq);
|
||||
+ irqdata = irq_get_irq_data(irq);
|
||||
+
|
||||
+ if (irqdata && irqdata->chip) {
|
||||
+ irqchip = irqdata->chip;
|
||||
+ } else {
|
||||
+ ret = -ENODEV;
|
||||
+ goto exit_gpio_free_in_pin;
|
||||
+ }
|
||||
+
|
||||
+ /* if pin is high, then this must be an active low receiver. */
|
||||
+ if (sense == -1) {
|
||||
+ /* wait 1/2 sec for the power supply */
|
||||
+ msleep(500);
|
||||
+
|
||||
+ /*
|
||||
+ * probe 9 times every 0.04s, collect "votes" for
|
||||
+ * active high/low
|
||||
+ */
|
||||
+ nlow = 0;
|
||||
+ nhigh = 0;
|
||||
+ for (i = 0; i < 9; i++) {
|
||||
+ if (gpiochip->get(gpiochip, gpio_in_pin))
|
||||
+ nlow++;
|
||||
+ else
|
||||
+ nhigh++;
|
||||
+ msleep(40);
|
||||
+ }
|
||||
+ sense = (nlow >= nhigh ? 1 : 0);
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME
|
||||
+ ": auto-detected active %s receiver on GPIO pin %d\n",
|
||||
+ sense ? "low" : "high", gpio_in_pin);
|
||||
+ } else {
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME
|
||||
+ ": manually using active %s receiver on GPIO pin %d\n",
|
||||
+ sense ? "low" : "high", gpio_in_pin);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+ exit_gpio_free_in_pin:
|
||||
+ gpio_free(gpio_in_pin);
|
||||
+
|
||||
+ exit_gpio_free_out_pin:
|
||||
+ gpio_free(gpio_out_pin);
|
||||
+
|
||||
+ exit_init_port:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+// called when the character device is opened
|
||||
+static int set_use_inc(void *data)
|
||||
+{
|
||||
+ int result;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ /* initialize timestamp */
|
||||
+ do_gettimeofday(&lasttv);
|
||||
+
|
||||
+ result = request_irq(gpiochip->to_irq(gpiochip, gpio_in_pin),
|
||||
+ (irq_handler_t) irq_handler, 0,
|
||||
+ LIRC_DRIVER_NAME, (void*) 0);
|
||||
+
|
||||
+ switch (result) {
|
||||
+ case -EBUSY:
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": IRQ %d is busy\n",
|
||||
+ gpiochip->to_irq(gpiochip, gpio_in_pin));
|
||||
+ return -EBUSY;
|
||||
+ case -EINVAL:
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": Bad irq number or handler\n");
|
||||
+ return -EINVAL;
|
||||
+ default:
|
||||
+ dprintk("Interrupt %d obtained\n",
|
||||
+ gpiochip->to_irq(gpiochip, gpio_in_pin));
|
||||
+ break;
|
||||
+ };
|
||||
+
|
||||
+ /* initialize pulse/space widths */
|
||||
+ init_timing_params(duty_cycle, freq);
|
||||
+
|
||||
+ spin_lock_irqsave(&lock, flags);
|
||||
+
|
||||
+ /* GPIO Pin Falling/Rising Edge Detect Enable */
|
||||
+ irqchip->irq_set_type(irqdata,
|
||||
+ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING);
|
||||
+
|
||||
+ /* unmask the irq */
|
||||
+ irqchip->irq_unmask(irqdata);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void set_use_dec(void *data)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&lock, flags);
|
||||
+
|
||||
+ /* GPIO Pin Falling/Rising Edge Detect Disable */
|
||||
+ irqchip->irq_set_type(irqdata, 0);
|
||||
+ irqchip->irq_mask(irqdata);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&lock, flags);
|
||||
+
|
||||
+ free_irq(gpiochip->to_irq(gpiochip, gpio_in_pin), (void *) 0);
|
||||
+
|
||||
+ dprintk(KERN_INFO LIRC_DRIVER_NAME
|
||||
+ ": freed IRQ %d\n", gpiochip->to_irq(gpiochip, gpio_in_pin));
|
||||
+}
|
||||
+
|
||||
+static ssize_t lirc_write(struct file *file, const char *buf,
|
||||
+ size_t n, loff_t *ppos)
|
||||
+{
|
||||
+ int i, count;
|
||||
+ unsigned long flags;
|
||||
+ long delta = 0;
|
||||
+ int *wbuf;
|
||||
+
|
||||
+ count = n / sizeof(int);
|
||||
+ if (n % sizeof(int) || count % 2 == 0)
|
||||
+ return -EINVAL;
|
||||
+ wbuf = memdup_user(buf, n);
|
||||
+ if (IS_ERR(wbuf))
|
||||
+ return PTR_ERR(wbuf);
|
||||
+ spin_lock_irqsave(&lock, flags);
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (i%2)
|
||||
+ send_space(wbuf[i] - delta);
|
||||
+ else
|
||||
+ delta = send_pulse(wbuf[i]);
|
||||
+ }
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&lock, flags);
|
||||
+ kfree(wbuf);
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
||||
+{
|
||||
+ int result;
|
||||
+ __u32 value;
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case LIRC_GET_SEND_MODE:
|
||||
+ return -ENOIOCTLCMD;
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_SET_SEND_MODE:
|
||||
+ result = get_user(value, (__u32 *) arg);
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+ /* only LIRC_MODE_PULSE supported */
|
||||
+ if (value != LIRC_MODE_PULSE)
|
||||
+ return -ENOSYS;
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_GET_LENGTH:
|
||||
+ return -ENOSYS;
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_SET_SEND_DUTY_CYCLE:
|
||||
+ dprintk("SET_SEND_DUTY_CYCLE\n");
|
||||
+ result = get_user(value, (__u32 *) arg);
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+ if (value <= 0 || value > 100)
|
||||
+ return -EINVAL;
|
||||
+ return init_timing_params(value, freq);
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_SET_SEND_CARRIER:
|
||||
+ dprintk("SET_SEND_CARRIER\n");
|
||||
+ result = get_user(value, (__u32 *) arg);
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+ if (value > 500000 || value < 20000)
|
||||
+ return -EINVAL;
|
||||
+ return init_timing_params(duty_cycle, value);
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ return lirc_dev_fop_ioctl(filep, cmd, arg);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct file_operations lirc_fops = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .write = lirc_write,
|
||||
+ .unlocked_ioctl = lirc_ioctl,
|
||||
+ .read = lirc_dev_fop_read,
|
||||
+ .poll = lirc_dev_fop_poll,
|
||||
+ .open = lirc_dev_fop_open,
|
||||
+ .release = lirc_dev_fop_close,
|
||||
+ .llseek = no_llseek,
|
||||
+};
|
||||
+
|
||||
+static struct lirc_driver driver = {
|
||||
+ .name = LIRC_DRIVER_NAME,
|
||||
+ .minor = -1,
|
||||
+ .code_length = 1,
|
||||
+ .sample_rate = 0,
|
||||
+ .data = NULL,
|
||||
+ .add_to_buf = NULL,
|
||||
+ .rbuf = &rbuf,
|
||||
+ .set_use_inc = set_use_inc,
|
||||
+ .set_use_dec = set_use_dec,
|
||||
+ .fops = &lirc_fops,
|
||||
+ .dev = NULL,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static struct platform_driver lirc_rpi_driver = {
|
||||
+ .driver = {
|
||||
+ .name = LIRC_DRIVER_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init lirc_rpi_init(void)
|
||||
+{
|
||||
+ int result;
|
||||
+
|
||||
+ /* Init read buffer. */
|
||||
+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
|
||||
+ if (result < 0)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ result = platform_driver_register(&lirc_rpi_driver);
|
||||
+ if (result) {
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": lirc register returned %d\n", result);
|
||||
+ goto exit_buffer_free;
|
||||
+ }
|
||||
+
|
||||
+ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
|
||||
+ if (!lirc_rpi_dev) {
|
||||
+ result = -ENOMEM;
|
||||
+ goto exit_driver_unregister;
|
||||
+ }
|
||||
+
|
||||
+ result = platform_device_add(lirc_rpi_dev);
|
||||
+ if (result)
|
||||
+ goto exit_device_put;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+ exit_device_put:
|
||||
+ platform_device_put(lirc_rpi_dev);
|
||||
+
|
||||
+ exit_driver_unregister:
|
||||
+ platform_driver_unregister(&lirc_rpi_driver);
|
||||
+
|
||||
+ exit_buffer_free:
|
||||
+ lirc_buffer_free(&rbuf);
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+static void lirc_rpi_exit(void)
|
||||
+{
|
||||
+ gpio_free(gpio_out_pin);
|
||||
+ gpio_free(gpio_in_pin);
|
||||
+ platform_device_unregister(lirc_rpi_dev);
|
||||
+ platform_driver_unregister(&lirc_rpi_driver);
|
||||
+ lirc_buffer_free(&rbuf);
|
||||
+}
|
||||
+
|
||||
+static int __init lirc_rpi_init_module(void)
|
||||
+{
|
||||
+ int result, i;
|
||||
+
|
||||
+ result = lirc_rpi_init();
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+
|
||||
+ /* check if the module received valid gpio pin numbers */
|
||||
+ result = 0;
|
||||
+ if (gpio_in_pin != gpio_out_pin) {
|
||||
+ for(i = 0; (i < ARRAY_SIZE(valid_gpio_pins)) && (result != 2); i++) {
|
||||
+ if (gpio_in_pin == valid_gpio_pins[i] ||
|
||||
+ gpio_out_pin == valid_gpio_pins[i]) {
|
||||
+ result++;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (result != 2) {
|
||||
+ result = -EINVAL;
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": invalid GPIO pin(s) specified!\n");
|
||||
+ goto exit_rpi;
|
||||
+ }
|
||||
+
|
||||
+ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE |
|
||||
+ LIRC_CAN_SET_SEND_CARRIER |
|
||||
+ LIRC_CAN_SEND_PULSE |
|
||||
+ LIRC_CAN_REC_MODE2;
|
||||
+
|
||||
+ driver.dev = &lirc_rpi_dev->dev;
|
||||
+ driver.minor = lirc_register_driver(&driver);
|
||||
+
|
||||
+ if (driver.minor < 0) {
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": device registration failed with %d\n", result);
|
||||
+ result = -EIO;
|
||||
+ goto exit_rpi;
|
||||
+ }
|
||||
+
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n");
|
||||
+
|
||||
+ result = init_port();
|
||||
+ if (result < 0)
|
||||
+ goto exit_rpi;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+ exit_rpi:
|
||||
+ lirc_rpi_exit();
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+static void __exit lirc_rpi_exit_module(void)
|
||||
+{
|
||||
+ lirc_rpi_exit();
|
||||
+
|
||||
+ lirc_unregister_driver(driver.minor);
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n");
|
||||
+}
|
||||
+
|
||||
+module_init(lirc_rpi_init_module);
|
||||
+module_exit(lirc_rpi_exit_module);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO.");
|
||||
+MODULE_AUTHOR("Aron Robert Szabo <aron@reon.hu>");
|
||||
+MODULE_AUTHOR("Michael Bishop <cleverca22@gmail.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
+module_param(gpio_out_pin, int, S_IRUGO);
|
||||
+MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM"
|
||||
+ " processor. Valid pin numbers are: 0, 1, 4, 8, 7, 9, 10, 11,"
|
||||
+ " 14, 15, 17, 18, 21, 22, 23, 24, 25, default 17");
|
||||
+
|
||||
+module_param(gpio_in_pin, int, S_IRUGO);
|
||||
+MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor."
|
||||
+ " Valid pin numbers are: 0, 1, 4, 8, 7, 9, 10, 11, 14, 15,"
|
||||
+ " 17, 18, 21, 22, 23, 24, 25, default 18");
|
||||
+
|
||||
+module_param(sense, bool, S_IRUGO);
|
||||
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
|
||||
+ " (0 = active high, 1 = active low )");
|
||||
+
|
||||
+module_param(softcarrier, bool, S_IRUGO);
|
||||
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
|
||||
+
|
||||
+module_param(invert, bool, S_IRUGO);
|
||||
+MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off");
|
||||
+
|
||||
+module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
+MODULE_PARM_DESC(debug, "Enable debugging messages");
|
||||
diff -urN linux-3.10/drivers/staging/media/lirc/Makefile linux-rpi-3.10.y/drivers/staging/media/lirc/Makefile
|
||||
--- linux-3.10/drivers/staging/media/lirc/Makefile 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/staging/media/lirc/Makefile 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -7,6 +7,7 @@
|
||||
obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
|
||||
obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
|
||||
obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
|
||||
+obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o
|
||||
obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
|
||||
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
|
||||
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
|
|
@ -0,0 +1,659 @@
|
|||
diff -urN linux-3.10/drivers/spi/Kconfig linux-rpi-3.10.y/drivers/spi/Kconfig
|
||||
--- linux-3.10/drivers/spi/Kconfig 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/spi/Kconfig 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -86,6 +86,14 @@
|
||||
is for the regular SPI controller. Slave mode operation is not also
|
||||
not supported.
|
||||
|
||||
+config SPI_BCM2708
|
||||
+ tristate "BCM2708 SPI controller driver (SPI0)"
|
||||
+ depends on MACH_BCM2708
|
||||
+ help
|
||||
+ This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This
|
||||
+ driver is not compatible with the "Universal SPI Master" or the SPI slave
|
||||
+ device.
|
||||
+
|
||||
config SPI_BFIN5XX
|
||||
tristate "SPI controller driver for ADI Blackfin5xx"
|
||||
depends on BLACKFIN
|
||||
diff -urN linux-3.10/drivers/spi/Makefile linux-rpi-3.10.y/drivers/spi/Makefile
|
||||
--- linux-3.10/drivers/spi/Makefile 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/spi/Makefile 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -17,6 +17,7 @@
|
||||
obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
|
||||
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
|
||||
obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
|
||||
+obj-$(CONFIG_SPI_BCM2708) += spi-bcm2708.o
|
||||
obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
|
||||
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
|
||||
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
|
||||
diff -urN linux-3.10/drivers/spi/spi-bcm2708.c linux-rpi-3.10.y/drivers/spi/spi-bcm2708.c
|
||||
--- linux-3.10/drivers/spi/spi-bcm2708.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/spi/spi-bcm2708.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -0,0 +1,626 @@
|
||||
+/*
|
||||
+ * Driver for Broadcom BCM2708 SPI Controllers
|
||||
+ *
|
||||
+ * Copyright (C) 2012 Chris Boot
|
||||
+ *
|
||||
+ * This driver is inspired by:
|
||||
+ * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * spi-atmel.c, Copyright (C) 2006 Atmel Corporation
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License
|
||||
+ * along with this program; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/spi/spi.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/log2.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/wait.h>
|
||||
+
|
||||
+/* SPI register offsets */
|
||||
+#define SPI_CS 0x00
|
||||
+#define SPI_FIFO 0x04
|
||||
+#define SPI_CLK 0x08
|
||||
+#define SPI_DLEN 0x0c
|
||||
+#define SPI_LTOH 0x10
|
||||
+#define SPI_DC 0x14
|
||||
+
|
||||
+/* Bitfields in CS */
|
||||
+#define SPI_CS_LEN_LONG 0x02000000
|
||||
+#define SPI_CS_DMA_LEN 0x01000000
|
||||
+#define SPI_CS_CSPOL2 0x00800000
|
||||
+#define SPI_CS_CSPOL1 0x00400000
|
||||
+#define SPI_CS_CSPOL0 0x00200000
|
||||
+#define SPI_CS_RXF 0x00100000
|
||||
+#define SPI_CS_RXR 0x00080000
|
||||
+#define SPI_CS_TXD 0x00040000
|
||||
+#define SPI_CS_RXD 0x00020000
|
||||
+#define SPI_CS_DONE 0x00010000
|
||||
+#define SPI_CS_LEN 0x00002000
|
||||
+#define SPI_CS_REN 0x00001000
|
||||
+#define SPI_CS_ADCS 0x00000800
|
||||
+#define SPI_CS_INTR 0x00000400
|
||||
+#define SPI_CS_INTD 0x00000200
|
||||
+#define SPI_CS_DMAEN 0x00000100
|
||||
+#define SPI_CS_TA 0x00000080
|
||||
+#define SPI_CS_CSPOL 0x00000040
|
||||
+#define SPI_CS_CLEAR_RX 0x00000020
|
||||
+#define SPI_CS_CLEAR_TX 0x00000010
|
||||
+#define SPI_CS_CPOL 0x00000008
|
||||
+#define SPI_CS_CPHA 0x00000004
|
||||
+#define SPI_CS_CS_10 0x00000002
|
||||
+#define SPI_CS_CS_01 0x00000001
|
||||
+
|
||||
+#define SPI_TIMEOUT_MS 150
|
||||
+
|
||||
+#define DRV_NAME "bcm2708_spi"
|
||||
+
|
||||
+struct bcm2708_spi {
|
||||
+ spinlock_t lock;
|
||||
+ void __iomem *base;
|
||||
+ int irq;
|
||||
+ struct clk *clk;
|
||||
+ bool stopping;
|
||||
+
|
||||
+ struct list_head queue;
|
||||
+ struct workqueue_struct *workq;
|
||||
+ struct work_struct work;
|
||||
+ struct completion done;
|
||||
+
|
||||
+ const u8 *tx_buf;
|
||||
+ u8 *rx_buf;
|
||||
+ int len;
|
||||
+};
|
||||
+
|
||||
+struct bcm2708_spi_state {
|
||||
+ u32 cs;
|
||||
+ u16 cdiv;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * This function sets the ALT mode on the SPI pins so that we can use them with
|
||||
+ * the SPI hardware.
|
||||
+ *
|
||||
+ * FIXME: This is a hack. Use pinmux / pinctrl.
|
||||
+ */
|
||||
+static void bcm2708_init_pinmode(void)
|
||||
+{
|
||||
+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
||||
+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
||||
+
|
||||
+ int pin;
|
||||
+ u32 *gpio = ioremap(0x20200000, SZ_16K);
|
||||
+
|
||||
+ /* SPI is on GPIO 7..11 */
|
||||
+ for (pin = 7; pin <= 11; pin++) {
|
||||
+ INP_GPIO(pin); /* set mode to GPIO input first */
|
||||
+ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */
|
||||
+ }
|
||||
+
|
||||
+ iounmap(gpio);
|
||||
+
|
||||
+#undef INP_GPIO
|
||||
+#undef SET_GPIO_ALT
|
||||
+}
|
||||
+
|
||||
+static inline u32 bcm2708_rd(struct bcm2708_spi *bs, unsigned reg)
|
||||
+{
|
||||
+ return readl(bs->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_wr(struct bcm2708_spi *bs, unsigned reg, u32 val)
|
||||
+{
|
||||
+ writel(val, bs->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_rd_fifo(struct bcm2708_spi *bs, int len)
|
||||
+{
|
||||
+ u8 byte;
|
||||
+
|
||||
+ while (len--) {
|
||||
+ byte = bcm2708_rd(bs, SPI_FIFO);
|
||||
+ if (bs->rx_buf)
|
||||
+ *bs->rx_buf++ = byte;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_wr_fifo(struct bcm2708_spi *bs, int len)
|
||||
+{
|
||||
+ u8 byte;
|
||||
+ u16 val;
|
||||
+
|
||||
+ if (len > bs->len)
|
||||
+ len = bs->len;
|
||||
+
|
||||
+ if (unlikely(bcm2708_rd(bs, SPI_CS) & SPI_CS_LEN)) {
|
||||
+ /* LoSSI mode */
|
||||
+ if (unlikely(len % 2)) {
|
||||
+ printk(KERN_ERR"bcm2708_wr_fifo: length must be even, skipping.\n");
|
||||
+ bs->len = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+ while (len) {
|
||||
+ if (bs->tx_buf) {
|
||||
+ val = *(const u16 *)bs->tx_buf;
|
||||
+ bs->tx_buf += 2;
|
||||
+ } else
|
||||
+ val = 0;
|
||||
+ bcm2708_wr(bs, SPI_FIFO, val);
|
||||
+ bs->len -= 2;
|
||||
+ len -= 2;
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ while (len--) {
|
||||
+ byte = bs->tx_buf ? *bs->tx_buf++ : 0;
|
||||
+ bcm2708_wr(bs, SPI_FIFO, byte);
|
||||
+ bs->len--;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t bcm2708_spi_interrupt(int irq, void *dev_id)
|
||||
+{
|
||||
+ struct spi_master *master = dev_id;
|
||||
+ struct bcm2708_spi *bs = spi_master_get_devdata(master);
|
||||
+ u32 cs;
|
||||
+
|
||||
+ spin_lock(&bs->lock);
|
||||
+
|
||||
+ cs = bcm2708_rd(bs, SPI_CS);
|
||||
+
|
||||
+ if (cs & SPI_CS_DONE) {
|
||||
+ if (bs->len) { /* first interrupt in a transfer */
|
||||
+ /* fill the TX fifo with up to 16 bytes */
|
||||
+ bcm2708_wr_fifo(bs, 16);
|
||||
+ } else { /* transfer complete */
|
||||
+ /* disable interrupts */
|
||||
+ cs &= ~(SPI_CS_INTR | SPI_CS_INTD);
|
||||
+ bcm2708_wr(bs, SPI_CS, cs);
|
||||
+
|
||||
+ /* drain RX FIFO */
|
||||
+ while (cs & SPI_CS_RXD) {
|
||||
+ bcm2708_rd_fifo(bs, 1);
|
||||
+ cs = bcm2708_rd(bs, SPI_CS);
|
||||
+ }
|
||||
+
|
||||
+ /* wake up our bh */
|
||||
+ complete(&bs->done);
|
||||
+ }
|
||||
+ } else if (cs & SPI_CS_RXR) {
|
||||
+ /* read 12 bytes of data */
|
||||
+ bcm2708_rd_fifo(bs, 12);
|
||||
+
|
||||
+ /* write up to 12 bytes */
|
||||
+ bcm2708_wr_fifo(bs, 12);
|
||||
+ }
|
||||
+
|
||||
+ spin_unlock(&bs->lock);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_setup_state(struct spi_master *master,
|
||||
+ struct device *dev, struct bcm2708_spi_state *state,
|
||||
+ u32 hz, u8 csel, u8 mode, u8 bpw)
|
||||
+{
|
||||
+ struct bcm2708_spi *bs = spi_master_get_devdata(master);
|
||||
+ int cdiv;
|
||||
+ unsigned long bus_hz;
|
||||
+ u32 cs = 0;
|
||||
+
|
||||
+ bus_hz = clk_get_rate(bs->clk);
|
||||
+
|
||||
+ if (hz >= bus_hz) {
|
||||
+ cdiv = 2; /* bus_hz / 2 is as fast as we can go */
|
||||
+ } else if (hz) {
|
||||
+ cdiv = DIV_ROUND_UP(bus_hz, hz);
|
||||
+
|
||||
+ /* CDIV must be a power of 2, so round up */
|
||||
+ cdiv = roundup_pow_of_two(cdiv);
|
||||
+
|
||||
+ if (cdiv > 65536) {
|
||||
+ dev_dbg(dev,
|
||||
+ "setup: %d Hz too slow, cdiv %u; min %ld Hz\n",
|
||||
+ hz, cdiv, bus_hz / 65536);
|
||||
+ return -EINVAL;
|
||||
+ } else if (cdiv == 65536) {
|
||||
+ cdiv = 0;
|
||||
+ } else if (cdiv == 1) {
|
||||
+ cdiv = 2; /* 1 gets rounded down to 0; == 65536 */
|
||||
+ }
|
||||
+ } else {
|
||||
+ cdiv = 0;
|
||||
+ }
|
||||
+
|
||||
+ switch (bpw) {
|
||||
+ case 8:
|
||||
+ break;
|
||||
+ case 9:
|
||||
+ /* Reading in LoSSI mode is a special case. See 'BCM2835 ARM Peripherals' datasheet */
|
||||
+ cs |= SPI_CS_LEN;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_dbg(dev, "setup: invalid bits_per_word %u (must be 8 or 9)\n",
|
||||
+ bpw);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (mode & SPI_CPOL)
|
||||
+ cs |= SPI_CS_CPOL;
|
||||
+ if (mode & SPI_CPHA)
|
||||
+ cs |= SPI_CS_CPHA;
|
||||
+
|
||||
+ if (!(mode & SPI_NO_CS)) {
|
||||
+ if (mode & SPI_CS_HIGH) {
|
||||
+ cs |= SPI_CS_CSPOL;
|
||||
+ cs |= SPI_CS_CSPOL0 << csel;
|
||||
+ }
|
||||
+
|
||||
+ cs |= csel;
|
||||
+ } else {
|
||||
+ cs |= SPI_CS_CS_10 | SPI_CS_CS_01;
|
||||
+ }
|
||||
+
|
||||
+ if (state) {
|
||||
+ state->cs = cs;
|
||||
+ state->cdiv = cdiv;
|
||||
+ dev_dbg(dev, "setup: want %d Hz; "
|
||||
+ "bus_hz=%lu / cdiv=%u == %lu Hz; "
|
||||
+ "mode %u: cs 0x%08X\n",
|
||||
+ hz, bus_hz, cdiv, bus_hz/cdiv, mode, cs);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_process_transfer(struct bcm2708_spi *bs,
|
||||
+ struct spi_message *msg, struct spi_transfer *xfer)
|
||||
+{
|
||||
+ struct spi_device *spi = msg->spi;
|
||||
+ struct bcm2708_spi_state state, *stp;
|
||||
+ int ret;
|
||||
+ u32 cs;
|
||||
+
|
||||
+ if (bs->stopping)
|
||||
+ return -ESHUTDOWN;
|
||||
+
|
||||
+ if (xfer->bits_per_word || xfer->speed_hz) {
|
||||
+ ret = bcm2708_setup_state(spi->master, &spi->dev, &state,
|
||||
+ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz,
|
||||
+ spi->chip_select, spi->mode,
|
||||
+ xfer->bits_per_word ? xfer->bits_per_word :
|
||||
+ spi->bits_per_word);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ stp = &state;
|
||||
+ } else {
|
||||
+ stp = spi->controller_state;
|
||||
+ }
|
||||
+
|
||||
+ INIT_COMPLETION(bs->done);
|
||||
+ bs->tx_buf = xfer->tx_buf;
|
||||
+ bs->rx_buf = xfer->rx_buf;
|
||||
+ bs->len = xfer->len;
|
||||
+
|
||||
+ cs = stp->cs | SPI_CS_INTR | SPI_CS_INTD | SPI_CS_TA;
|
||||
+
|
||||
+ bcm2708_wr(bs, SPI_CLK, stp->cdiv);
|
||||
+ bcm2708_wr(bs, SPI_CS, cs);
|
||||
+
|
||||
+ ret = wait_for_completion_timeout(&bs->done,
|
||||
+ msecs_to_jiffies(SPI_TIMEOUT_MS));
|
||||
+ if (ret == 0) {
|
||||
+ dev_err(&spi->dev, "transfer timed out\n");
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ if (xfer->delay_usecs)
|
||||
+ udelay(xfer->delay_usecs);
|
||||
+
|
||||
+ if (list_is_last(&xfer->transfer_list, &msg->transfers) ||
|
||||
+ xfer->cs_change) {
|
||||
+ /* clear TA and interrupt flags */
|
||||
+ bcm2708_wr(bs, SPI_CS, stp->cs);
|
||||
+ }
|
||||
+
|
||||
+ msg->actual_length += (xfer->len - bs->len);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct bcm2708_spi *bs = container_of(work, struct bcm2708_spi, work);
|
||||
+ unsigned long flags;
|
||||
+ struct spi_message *msg;
|
||||
+ struct spi_transfer *xfer;
|
||||
+ int status = 0;
|
||||
+
|
||||
+ spin_lock_irqsave(&bs->lock, flags);
|
||||
+ while (!list_empty(&bs->queue)) {
|
||||
+ msg = list_first_entry(&bs->queue, struct spi_message, queue);
|
||||
+ list_del_init(&msg->queue);
|
||||
+ spin_unlock_irqrestore(&bs->lock, flags);
|
||||
+
|
||||
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
+ status = bcm2708_process_transfer(bs, msg, xfer);
|
||||
+ if (status)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ msg->status = status;
|
||||
+ msg->complete(msg->context);
|
||||
+
|
||||
+ spin_lock_irqsave(&bs->lock, flags);
|
||||
+ }
|
||||
+ spin_unlock_irqrestore(&bs->lock, flags);
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_spi_setup(struct spi_device *spi)
|
||||
+{
|
||||
+ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master);
|
||||
+ struct bcm2708_spi_state *state;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (bs->stopping)
|
||||
+ return -ESHUTDOWN;
|
||||
+
|
||||
+ if (!(spi->mode & SPI_NO_CS) &&
|
||||
+ (spi->chip_select > spi->master->num_chipselect)) {
|
||||
+ dev_dbg(&spi->dev,
|
||||
+ "setup: invalid chipselect %u (%u defined)\n",
|
||||
+ spi->chip_select, spi->master->num_chipselect);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ state = spi->controller_state;
|
||||
+ if (!state) {
|
||||
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
+ if (!state)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ spi->controller_state = state;
|
||||
+ }
|
||||
+
|
||||
+ ret = bcm2708_setup_state(spi->master, &spi->dev, state,
|
||||
+ spi->max_speed_hz, spi->chip_select, spi->mode,
|
||||
+ spi->bits_per_word);
|
||||
+ if (ret < 0) {
|
||||
+ kfree(state);
|
||||
+ spi->controller_state = NULL;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ dev_dbg(&spi->dev,
|
||||
+ "setup: cd %d: %d Hz, bpw %u, mode 0x%x -> CS=%08x CDIV=%04x\n",
|
||||
+ spi->chip_select, spi->max_speed_hz, spi->bits_per_word,
|
||||
+ spi->mode, state->cs, state->cdiv);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg)
|
||||
+{
|
||||
+ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master);
|
||||
+ struct spi_transfer *xfer;
|
||||
+ int ret;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ if (unlikely(list_empty(&msg->transfers)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (bs->stopping)
|
||||
+ return -ESHUTDOWN;
|
||||
+
|
||||
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
+ if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) {
|
||||
+ dev_dbg(&spi->dev, "missing rx or tx buf\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (!xfer->bits_per_word || xfer->speed_hz)
|
||||
+ continue;
|
||||
+
|
||||
+ ret = bcm2708_setup_state(spi->master, &spi->dev, NULL,
|
||||
+ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz,
|
||||
+ spi->chip_select, spi->mode,
|
||||
+ xfer->bits_per_word ? xfer->bits_per_word :
|
||||
+ spi->bits_per_word);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ msg->status = -EINPROGRESS;
|
||||
+ msg->actual_length = 0;
|
||||
+
|
||||
+ spin_lock_irqsave(&bs->lock, flags);
|
||||
+ list_add_tail(&msg->queue, &bs->queue);
|
||||
+ queue_work(bs->workq, &bs->work);
|
||||
+ spin_unlock_irqrestore(&bs->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_spi_cleanup(struct spi_device *spi)
|
||||
+{
|
||||
+ if (spi->controller_state) {
|
||||
+ kfree(spi->controller_state);
|
||||
+ spi->controller_state = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_spi_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *regs;
|
||||
+ int irq, err = -ENOMEM;
|
||||
+ struct clk *clk;
|
||||
+ struct spi_master *master;
|
||||
+ struct bcm2708_spi *bs;
|
||||
+
|
||||
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ if (!regs) {
|
||||
+ dev_err(&pdev->dev, "could not get IO memory\n");
|
||||
+ return -ENXIO;
|
||||
+ }
|
||||
+
|
||||
+ irq = platform_get_irq(pdev, 0);
|
||||
+ if (irq < 0) {
|
||||
+ dev_err(&pdev->dev, "could not get IRQ\n");
|
||||
+ return irq;
|
||||
+ }
|
||||
+
|
||||
+ clk = clk_get(&pdev->dev, NULL);
|
||||
+ if (IS_ERR(clk)) {
|
||||
+ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk));
|
||||
+ return PTR_ERR(clk);
|
||||
+ }
|
||||
+
|
||||
+ bcm2708_init_pinmode();
|
||||
+
|
||||
+ master = spi_alloc_master(&pdev->dev, sizeof(*bs));
|
||||
+ if (!master) {
|
||||
+ dev_err(&pdev->dev, "spi_alloc_master() failed\n");
|
||||
+ goto out_clk_put;
|
||||
+ }
|
||||
+
|
||||
+ /* the spi->mode bits understood by this driver: */
|
||||
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS;
|
||||
+
|
||||
+ master->bus_num = pdev->id;
|
||||
+ master->num_chipselect = 3;
|
||||
+ master->setup = bcm2708_spi_setup;
|
||||
+ master->transfer = bcm2708_spi_transfer;
|
||||
+ master->cleanup = bcm2708_spi_cleanup;
|
||||
+ platform_set_drvdata(pdev, master);
|
||||
+
|
||||
+ bs = spi_master_get_devdata(master);
|
||||
+
|
||||
+ spin_lock_init(&bs->lock);
|
||||
+ INIT_LIST_HEAD(&bs->queue);
|
||||
+ init_completion(&bs->done);
|
||||
+ INIT_WORK(&bs->work, bcm2708_work);
|
||||
+
|
||||
+ bs->base = ioremap(regs->start, resource_size(regs));
|
||||
+ if (!bs->base) {
|
||||
+ dev_err(&pdev->dev, "could not remap memory\n");
|
||||
+ goto out_master_put;
|
||||
+ }
|
||||
+
|
||||
+ bs->workq = create_singlethread_workqueue(dev_name(&pdev->dev));
|
||||
+ if (!bs->workq) {
|
||||
+ dev_err(&pdev->dev, "could not create workqueue\n");
|
||||
+ goto out_iounmap;
|
||||
+ }
|
||||
+
|
||||
+ bs->irq = irq;
|
||||
+ bs->clk = clk;
|
||||
+ bs->stopping = false;
|
||||
+
|
||||
+ err = request_irq(irq, bcm2708_spi_interrupt, 0, dev_name(&pdev->dev),
|
||||
+ master);
|
||||
+ if (err) {
|
||||
+ dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
|
||||
+ goto out_workqueue;
|
||||
+ }
|
||||
+
|
||||
+ /* initialise the hardware */
|
||||
+ clk_enable(clk);
|
||||
+ bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX);
|
||||
+
|
||||
+ err = spi_register_master(master);
|
||||
+ if (err) {
|
||||
+ dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
|
||||
+ goto out_free_irq;
|
||||
+ }
|
||||
+
|
||||
+ dev_info(&pdev->dev, "SPI Controller at 0x%08lx (irq %d)\n",
|
||||
+ (unsigned long)regs->start, irq);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+out_free_irq:
|
||||
+ free_irq(bs->irq, master);
|
||||
+out_workqueue:
|
||||
+ destroy_workqueue(bs->workq);
|
||||
+out_iounmap:
|
||||
+ iounmap(bs->base);
|
||||
+out_master_put:
|
||||
+ spi_master_put(master);
|
||||
+out_clk_put:
|
||||
+ clk_put(clk);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_spi_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct spi_master *master = platform_get_drvdata(pdev);
|
||||
+ struct bcm2708_spi *bs = spi_master_get_devdata(master);
|
||||
+
|
||||
+ /* reset the hardware and block queue progress */
|
||||
+ spin_lock_irq(&bs->lock);
|
||||
+ bs->stopping = true;
|
||||
+ bcm2708_wr(bs, SPI_CS, SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX);
|
||||
+ spin_unlock_irq(&bs->lock);
|
||||
+
|
||||
+ flush_work_sync(&bs->work);
|
||||
+
|
||||
+ clk_disable(bs->clk);
|
||||
+ clk_put(bs->clk);
|
||||
+ free_irq(bs->irq, master);
|
||||
+ iounmap(bs->base);
|
||||
+
|
||||
+ spi_unregister_master(master);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver bcm2708_spi_driver = {
|
||||
+ .driver = {
|
||||
+ .name = DRV_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .probe = bcm2708_spi_probe,
|
||||
+ .remove = bcm2708_spi_remove,
|
||||
+};
|
||||
+
|
||||
+
|
||||
+static int __init bcm2708_spi_init(void)
|
||||
+{
|
||||
+ return platform_driver_probe(&bcm2708_spi_driver, bcm2708_spi_probe);
|
||||
+}
|
||||
+module_init(bcm2708_spi_init);
|
||||
+
|
||||
+static void __exit bcm2708_spi_exit(void)
|
||||
+{
|
||||
+ platform_driver_unregister(&bcm2708_spi_driver);
|
||||
+}
|
||||
+module_exit(bcm2708_spi_exit);
|
||||
+
|
||||
+
|
||||
+//module_platform_driver(bcm2708_spi_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2708");
|
||||
+MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:" DRV_NAME);
|
|
@ -0,0 +1,212 @@
|
|||
diff -urN linux-3.10/drivers/thermal/bcm2835-thermal.c linux-rpi-3.10.y/drivers/thermal/bcm2835-thermal.c
|
||||
--- linux-3.10/drivers/thermal/bcm2835-thermal.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/thermal/bcm2835-thermal.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -0,0 +1,184 @@
|
||||
+/*****************************************************************************
|
||||
+* Copyright 2011 Broadcom Corporation. All rights reserved.
|
||||
+*
|
||||
+* Unless you and Broadcom execute a separate written software license
|
||||
+* agreement governing use of this software, this software is licensed to you
|
||||
+* under the terms of the GNU General Public License version 2, available at
|
||||
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||
+*
|
||||
+* Notwithstanding the above, under no circumstances may you combine this
|
||||
+* software in any way with any other Broadcom software provided under a
|
||||
+* license other than the GPL, without Broadcom's express prior written
|
||||
+* consent.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/sysfs.h>
|
||||
+#include <mach/vcio.h>
|
||||
+#include <linux/thermal.h>
|
||||
+
|
||||
+
|
||||
+/* --- DEFINITIONS --- */
|
||||
+#define MODULE_NAME "bcm2835_thermal"
|
||||
+
|
||||
+/*#define THERMAL_DEBUG_ENABLE*/
|
||||
+
|
||||
+#ifdef THERMAL_DEBUG_ENABLE
|
||||
+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
|
||||
+#else
|
||||
+#define print_debug(fmt,...)
|
||||
+#endif
|
||||
+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
|
||||
+
|
||||
+#define VC_TAG_GET_TEMP 0x00030006
|
||||
+#define VC_TAG_GET_MAX_TEMP 0x0003000A
|
||||
+
|
||||
+typedef enum {
|
||||
+ TEMP,
|
||||
+ MAX_TEMP,
|
||||
+} temp_type;
|
||||
+
|
||||
+/* --- STRUCTS --- */
|
||||
+/* tag part of the message */
|
||||
+struct vc_msg_tag {
|
||||
+ uint32_t tag_id; /* the tag ID for the temperature */
|
||||
+ uint32_t buffer_size; /* size of the buffer (should be 8) */
|
||||
+ uint32_t request_code; /* identifies message as a request (should be 0) */
|
||||
+ uint32_t id; /* extra ID field (should be 0) */
|
||||
+ uint32_t val; /* returned value of the temperature */
|
||||
+};
|
||||
+
|
||||
+/* message structure to be sent to videocore */
|
||||
+struct vc_msg {
|
||||
+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */
|
||||
+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
|
||||
+ struct vc_msg_tag tag; /* the tag structure above to make */
|
||||
+ uint32_t end_tag; /* an end identifier, should be set to NULL */
|
||||
+};
|
||||
+
|
||||
+struct bcm2835_thermal_data {
|
||||
+ struct thermal_zone_device *thermal_dev;
|
||||
+ struct vc_msg msg;
|
||||
+};
|
||||
+
|
||||
+/* --- GLOBALS --- */
|
||||
+static struct bcm2835_thermal_data bcm2835_data;
|
||||
+
|
||||
+/* Thermal Device Operations */
|
||||
+static struct thermal_zone_device_ops ops;
|
||||
+
|
||||
+/* --- FUNCTIONS --- */
|
||||
+
|
||||
+static int bcm2835_get_temp_or_max(struct thermal_zone_device *thermal_dev, unsigned long *temp, unsigned tag_id)
|
||||
+{
|
||||
+ int result = -1, retry = 3;
|
||||
+ print_debug("IN");
|
||||
+
|
||||
+ *temp = 0;
|
||||
+ while (result != 0 && retry-- > 0) {
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg);
|
||||
+
|
||||
+ /* prepare message */
|
||||
+ bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg;
|
||||
+ bcm2835_data.msg.tag.buffer_size = 8;
|
||||
+ bcm2835_data.msg.tag.tag_id = tag_id;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg);
|
||||
+ print_debug("Got %stemperature as %u (%d,%x)\n", tag_id==VC_TAG_GET_MAX_TEMP ? "max ":"", (uint)bcm2835_data.msg.tag.val, result, bcm2835_data.msg.request_code);
|
||||
+ if (!(bcm2835_data.msg.request_code & 0x80000000))
|
||||
+ result = -1;
|
||||
+ }
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in milli degrees C */
|
||||
+ if (result == 0)
|
||||
+ *temp = (uint)bcm2835_data.msg.tag.val;
|
||||
+ else
|
||||
+ print_err("Failed to get temperature! (%x:%d)\n", tag_id, result);
|
||||
+ print_debug("OUT");
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *temp)
|
||||
+{
|
||||
+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_TEMP);
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int trip_num, unsigned long *temp)
|
||||
+{
|
||||
+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_MAX_TEMP);
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type)
|
||||
+{
|
||||
+ *trip_type = THERMAL_TRIP_HOT;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode)
|
||||
+{
|
||||
+ *dev_mode = THERMAL_DEVICE_ENABLED;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ print_debug("IN");
|
||||
+ print_debug("THERMAL Driver has been probed!");
|
||||
+
|
||||
+ /* check that the device isn't null!*/
|
||||
+ if(pdev == NULL)
|
||||
+ {
|
||||
+ print_debug("Platform device is empty!");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if(!(bcm2835_data.thermal_dev = thermal_zone_device_register("bcm2835_thermal", 1, 0, NULL, &ops, NULL, 0, 0)))
|
||||
+ {
|
||||
+ print_debug("Unable to register the thermal device!");
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_thermal_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ print_debug("IN");
|
||||
+
|
||||
+ thermal_zone_device_unregister(bcm2835_data.thermal_dev);
|
||||
+
|
||||
+ print_debug("OUT");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct thermal_zone_device_ops ops = {
|
||||
+ .get_temp = bcm2835_get_temp,
|
||||
+ .get_trip_temp = bcm2835_get_max_temp,
|
||||
+ .get_trip_type = bcm2835_get_trip_type,
|
||||
+ .get_mode = bcm2835_get_mode,
|
||||
+};
|
||||
+
|
||||
+/* Thermal Driver */
|
||||
+static struct platform_driver bcm2835_thermal_driver = {
|
||||
+ .probe = bcm2835_thermal_probe,
|
||||
+ .remove = bcm2835_thermal_remove,
|
||||
+ .driver = {
|
||||
+ .name = "bcm2835_thermal",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Dorian Peake");
|
||||
+MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
|
||||
+
|
||||
+module_platform_driver(bcm2835_thermal_driver);
|
||||
diff -urN linux-3.10/drivers/thermal/Kconfig linux-rpi-3.10.y/drivers/thermal/Kconfig
|
||||
--- linux-3.10/drivers/thermal/Kconfig 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/thermal/Kconfig 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -169,4 +169,11 @@
|
||||
enforce idle time which results in more package C-state residency. The
|
||||
user interface is exposed via generic thermal framework.
|
||||
|
||||
+config THERMAL_BCM2835
|
||||
+ tristate "BCM2835 Thermal Driver"
|
||||
+ help
|
||||
+ This will enable temperature monitoring for the Broadcom BCM2835
|
||||
+ chip. If built as a module, it will be called 'bcm2835-thermal'.
|
||||
+
|
||||
endif
|
||||
+
|
||||
diff -urN linux-3.10/drivers/thermal/Makefile linux-rpi-3.10.y/drivers/thermal/Makefile
|
||||
--- linux-3.10/drivers/thermal/Makefile 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/thermal/Makefile 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -23,4 +23,5 @@
|
||||
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
|
||||
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
|
||||
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
||||
+obj-$(CONFIG_THERMAL_BCM2835) += bcm2835-thermal.o
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
diff -urN linux-3.10/drivers/cpufreq/bcm2835-cpufreq.c linux-rpi-3.10.y/drivers/cpufreq/bcm2835-cpufreq.c
|
||||
--- linux-3.10/drivers/cpufreq/bcm2835-cpufreq.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/cpufreq/bcm2835-cpufreq.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -0,0 +1,239 @@
|
||||
+/*****************************************************************************
|
||||
+* Copyright 2011 Broadcom Corporation. All rights reserved.
|
||||
+*
|
||||
+* Unless you and Broadcom execute a separate written software license
|
||||
+* agreement governing use of this software, this software is licensed to you
|
||||
+* under the terms of the GNU General Public License version 2, available at
|
||||
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||
+*
|
||||
+* Notwithstanding the above, under no circumstances may you combine this
|
||||
+* software in any way with any other Broadcom software provided under a
|
||||
+* license other than the GPL, without Broadcom's express prior written
|
||||
+* consent.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+/*****************************************************************************
|
||||
+* FILENAME: bcm2835-cpufreq.h
|
||||
+* DESCRIPTION: This driver dynamically manages the CPU Frequency of the ARM
|
||||
+* processor. Messages are sent to Videocore either setting or requesting the
|
||||
+* frequency of the ARM in order to match an appropiate frequency to the current
|
||||
+* usage of the processor. The policy which selects the frequency to use is
|
||||
+* defined in the kernel .config file, but can be changed during runtime.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+/* ---------- INCLUDES ---------- */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/cpufreq.h>
|
||||
+#include <mach/vcio.h>
|
||||
+
|
||||
+/* ---------- DEFINES ---------- */
|
||||
+/*#define CPUFREQ_DEBUG_ENABLE*/ /* enable debugging */
|
||||
+#define MODULE_NAME "bcm2835-cpufreq"
|
||||
+
|
||||
+#define VCMSG_ID_ARM_CLOCK 0x000000003 /* Clock/Voltage ID's */
|
||||
+
|
||||
+/* debug printk macros */
|
||||
+#ifdef CPUFREQ_DEBUG_ENABLE
|
||||
+#define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
|
||||
+#else
|
||||
+#define print_debug(fmt,...)
|
||||
+#endif
|
||||
+#define print_err(fmt,...) pr_err("%s:%s:%d: "fmt, MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
|
||||
+#define print_info(fmt,...) pr_info("%s: "fmt, MODULE_NAME, ##__VA_ARGS__)
|
||||
+
|
||||
+/* tag part of the message */
|
||||
+struct vc_msg_tag {
|
||||
+ uint32_t tag_id; /* the message id */
|
||||
+ uint32_t buffer_size; /* size of the buffer (which in this case is always 8 bytes) */
|
||||
+ uint32_t data_size; /* amount of data being sent or received */
|
||||
+ uint32_t dev_id; /* the ID of the clock/voltage to get or set */
|
||||
+ uint32_t val; /* the value (e.g. rate (in Hz)) to set */
|
||||
+};
|
||||
+
|
||||
+/* message structure to be sent to videocore */
|
||||
+struct vc_msg {
|
||||
+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */
|
||||
+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
|
||||
+ struct vc_msg_tag tag; /* the tag structure above to make */
|
||||
+ uint32_t end_tag; /* an end identifier, should be set to NULL */
|
||||
+};
|
||||
+
|
||||
+/* ---------- GLOBALS ---------- */
|
||||
+static struct cpufreq_driver bcm2835_cpufreq_driver; /* the cpufreq driver global */
|
||||
+
|
||||
+/*
|
||||
+ ===============================================
|
||||
+ clk_rate either gets or sets the clock rates.
|
||||
+ ===============================================
|
||||
+*/
|
||||
+static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate)
|
||||
+{
|
||||
+ int s, actual_rate=0;
|
||||
+ struct vc_msg msg;
|
||||
+
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&msg, 0, sizeof msg);
|
||||
+
|
||||
+ msg.msg_size = sizeof msg;
|
||||
+
|
||||
+ msg.tag.tag_id = VCMSG_SET_CLOCK_RATE;
|
||||
+ msg.tag.buffer_size = 8;
|
||||
+ msg.tag.data_size = 8; /* we're sending the clock ID and the new rates which is a total of 2 words */
|
||||
+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK;
|
||||
+ msg.tag.val = arm_rate * 1000;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ s = bcm_mailbox_property(&msg, sizeof msg);
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in KHz */
|
||||
+ if (s == 0 && (msg.request_code & 0x80000000))
|
||||
+ actual_rate = msg.tag.val/1000;
|
||||
+
|
||||
+ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, actual_rate);
|
||||
+ return actual_rate;
|
||||
+}
|
||||
+
|
||||
+static uint32_t bcm2835_cpufreq_get_clock(int tag)
|
||||
+{
|
||||
+ int s;
|
||||
+ int arm_rate = 0;
|
||||
+ struct vc_msg msg;
|
||||
+
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&msg, 0, sizeof msg);
|
||||
+
|
||||
+ msg.msg_size = sizeof msg;
|
||||
+ msg.tag.tag_id = tag;
|
||||
+ msg.tag.buffer_size = 8;
|
||||
+ msg.tag.data_size = 4; /* we're just sending the clock ID which is one word long */
|
||||
+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ s = bcm_mailbox_property(&msg, sizeof msg);
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in KHz */
|
||||
+ if (s == 0 && (msg.request_code & 0x80000000))
|
||||
+ arm_rate = msg.tag.val/1000;
|
||||
+
|
||||
+ print_debug("%s frequency = %d\n",
|
||||
+ tag == VCMSG_GET_CLOCK_RATE ? "Current":
|
||||
+ tag == VCMSG_GET_MIN_CLOCK ? "Min":
|
||||
+ tag == VCMSG_GET_MAX_CLOCK ? "Max":
|
||||
+ "Unexpected", arm_rate);
|
||||
+
|
||||
+ return arm_rate;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ ====================================================
|
||||
+ Module Initialisation registers the cpufreq driver
|
||||
+ ====================================================
|
||||
+*/
|
||||
+static int __init bcm2835_cpufreq_module_init(void)
|
||||
+{
|
||||
+ print_debug("IN\n");
|
||||
+ return cpufreq_register_driver(&bcm2835_cpufreq_driver);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ =============
|
||||
+ Module exit
|
||||
+ =============
|
||||
+*/
|
||||
+static void __exit bcm2835_cpufreq_module_exit(void)
|
||||
+{
|
||||
+ print_debug("IN\n");
|
||||
+ cpufreq_unregister_driver(&bcm2835_cpufreq_driver);
|
||||
+ return;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ ==============================================================
|
||||
+ Initialisation function sets up the CPU policy for first use
|
||||
+ ==============================================================
|
||||
+*/
|
||||
+static int bcm2835_cpufreq_driver_init(struct cpufreq_policy *policy)
|
||||
+{
|
||||
+ /* measured value of how long it takes to change frequency */
|
||||
+ policy->cpuinfo.transition_latency = 355000; /* ns */
|
||||
+
|
||||
+ /* now find out what the maximum and minimum frequencies are */
|
||||
+ policy->min = policy->cpuinfo.min_freq = bcm2835_cpufreq_get_clock(VCMSG_GET_MIN_CLOCK);
|
||||
+ policy->max = policy->cpuinfo.max_freq = bcm2835_cpufreq_get_clock(VCMSG_GET_MAX_CLOCK);
|
||||
+ policy->cur = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
|
||||
+
|
||||
+ print_info("min=%d max=%d cur=%d\n", policy->min, policy->max, policy->cur);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ =================================================================================
|
||||
+ Target function chooses the most appropriate frequency from the table to enable
|
||||
+ =================================================================================
|
||||
+*/
|
||||
+
|
||||
+static int bcm2835_cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation)
|
||||
+{
|
||||
+ unsigned int target = target_freq;
|
||||
+ unsigned int cur = policy->cur;
|
||||
+ print_debug("%s: min=%d max=%d cur=%d target=%d\n",policy->governor->name,policy->min,policy->max,policy->cur,target_freq);
|
||||
+
|
||||
+ /* if we are above min and using ondemand, then just use max */
|
||||
+ if (strcmp("ondemand", policy->governor->name)==0 && target > policy->min)
|
||||
+ target = policy->max;
|
||||
+ /* if the frequency is the same, just quit */
|
||||
+ if (target == policy->cur)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* otherwise were good to set the clock frequency */
|
||||
+ policy->cur = bcm2835_cpufreq_set_clock(policy->cur, target);
|
||||
+
|
||||
+ if (!policy->cur)
|
||||
+ {
|
||||
+ print_err("Error occurred setting a new frequency (%d)!\n", target);
|
||||
+ policy->cur = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ print_debug("Freq %d->%d (min=%d max=%d target=%d request=%d)\n", cur, policy->cur, policy->min, policy->max, target_freq, target);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned int bcm2835_cpufreq_driver_get(unsigned int cpu)
|
||||
+{
|
||||
+ unsigned int actual_rate = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
|
||||
+ print_debug("cpu=%d\n", actual_rate);
|
||||
+ return actual_rate;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ =================================================================================
|
||||
+ Verify ensures that when a policy is changed, it is suitable for the CPU to use
|
||||
+ =================================================================================
|
||||
+*/
|
||||
+
|
||||
+static int bcm2835_cpufreq_driver_verify(struct cpufreq_policy *policy)
|
||||
+{
|
||||
+ print_info("switching to governor %s\n", policy->governor->name);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* the CPUFreq driver */
|
||||
+static struct cpufreq_driver bcm2835_cpufreq_driver = {
|
||||
+ .name = "BCM2835 CPUFreq",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .init = bcm2835_cpufreq_driver_init,
|
||||
+ .verify = bcm2835_cpufreq_driver_verify,
|
||||
+ .target = bcm2835_cpufreq_driver_target,
|
||||
+ .get = bcm2835_cpufreq_driver_get
|
||||
+};
|
||||
+
|
||||
+MODULE_AUTHOR("Dorian Peake and Dom Cobley");
|
||||
+MODULE_DESCRIPTION("CPU frequency driver for BCM2835 chip");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
+module_init(bcm2835_cpufreq_module_init);
|
||||
+module_exit(bcm2835_cpufreq_module_exit);
|
||||
+
|
||||
diff -urN linux-3.10/drivers/cpufreq/Kconfig.arm linux-rpi-3.10.y/drivers/cpufreq/Kconfig.arm
|
||||
--- linux-3.10/drivers/cpufreq/Kconfig.arm 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/cpufreq/Kconfig.arm 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -150,3 +150,11 @@
|
||||
default y
|
||||
help
|
||||
This adds the CPUFreq driver support for SPEAr SOCs.
|
||||
+
|
||||
+config ARM_BCM2835_CPUFREQ
|
||||
+ bool "BCM2835 Driver"
|
||||
+ default y
|
||||
+ help
|
||||
+ This adds the CPUFreq driver for BCM2835
|
||||
+
|
||||
+ If in doubt, say N.
|
||||
diff -urN linux-3.10/drivers/cpufreq/Makefile linux-rpi-3.10.y/drivers/cpufreq/Makefile
|
||||
--- linux-3.10/drivers/cpufreq/Makefile 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/cpufreq/Makefile 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -72,6 +72,7 @@
|
||||
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
|
||||
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra-cpufreq.o
|
||||
+obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o
|
||||
|
||||
##################################################################################
|
||||
# PowerPC platform drivers
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,257 @@
|
|||
Index: linux-3.10/drivers/hwmon/bcm2835-hwmon.c
|
||||
===================================================================
|
||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ linux-3.10/drivers/hwmon/bcm2835-hwmon.c 2013-07-09 20:25:51.309324091 +0100
|
||||
@@ -0,0 +1,219 @@
|
||||
+/*****************************************************************************
|
||||
+* Copyright 2011 Broadcom Corporation. All rights reserved.
|
||||
+*
|
||||
+* Unless you and Broadcom execute a separate written software license
|
||||
+* agreement governing use of this software, this software is licensed to you
|
||||
+* under the terms of the GNU General Public License version 2, available at
|
||||
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||
+*
|
||||
+* Notwithstanding the above, under no circumstances may you combine this
|
||||
+* software in any way with any other Broadcom software provided under a
|
||||
+* license other than the GPL, without Broadcom's express prior written
|
||||
+* consent.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/hwmon.h>
|
||||
+#include <linux/hwmon-sysfs.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/sysfs.h>
|
||||
+#include <mach/vcio.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/err.h>
|
||||
+
|
||||
+#define MODULE_NAME "bcm2835_hwmon"
|
||||
+
|
||||
+/*#define HWMON_DEBUG_ENABLE*/
|
||||
+
|
||||
+#ifdef HWMON_DEBUG_ENABLE
|
||||
+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
|
||||
+#else
|
||||
+#define print_debug(fmt,...)
|
||||
+#endif
|
||||
+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
|
||||
+#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__)
|
||||
+
|
||||
+#define VC_TAG_GET_TEMP 0x00030006
|
||||
+#define VC_TAG_GET_MAX_TEMP 0x0003000A
|
||||
+
|
||||
+/* --- STRUCTS --- */
|
||||
+struct bcm2835_hwmon_data {
|
||||
+ struct device *hwmon_dev;
|
||||
+};
|
||||
+
|
||||
+/* tag part of the message */
|
||||
+struct vc_msg_tag {
|
||||
+ uint32_t tag_id; /* the tag ID for the temperature */
|
||||
+ uint32_t buffer_size; /* size of the buffer (should be 8) */
|
||||
+ uint32_t request_code; /* identifies message as a request (should be 0) */
|
||||
+ uint32_t id; /* extra ID field (should be 0) */
|
||||
+ uint32_t val; /* returned value of the temperature */
|
||||
+};
|
||||
+
|
||||
+/* message structure to be sent to videocore */
|
||||
+struct vc_msg {
|
||||
+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */
|
||||
+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
|
||||
+ struct vc_msg_tag tag; /* the tag structure above to make */
|
||||
+ uint32_t end_tag; /* an end identifier, should be set to NULL */
|
||||
+};
|
||||
+
|
||||
+typedef enum {
|
||||
+ TEMP,
|
||||
+ MAX_TEMP,
|
||||
+} temp_type;
|
||||
+
|
||||
+/* --- PROTOTYPES --- */
|
||||
+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
+
|
||||
+/* --- GLOBALS --- */
|
||||
+
|
||||
+static struct bcm2835_hwmon_data *bcm2835_data;
|
||||
+static struct platform_driver bcm2835_hwmon_driver;
|
||||
+
|
||||
+static SENSOR_DEVICE_ATTR(name, S_IRUGO,bcm2835_get_name,NULL,0);
|
||||
+static SENSOR_DEVICE_ATTR(temp1_input,S_IRUGO,bcm2835_get_temp,NULL,TEMP);
|
||||
+static SENSOR_DEVICE_ATTR(temp1_max,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP);
|
||||
+
|
||||
+static struct attribute* bcm2835_attributes[] = {
|
||||
+ &sensor_dev_attr_name.dev_attr.attr,
|
||||
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+static struct attribute_group bcm2835_attr_group = {
|
||||
+ .attrs = bcm2835_attributes,
|
||||
+};
|
||||
+
|
||||
+/* --- FUNCTIONS --- */
|
||||
+
|
||||
+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ return sprintf(buf,"bcm2835_hwmon\n");
|
||||
+}
|
||||
+
|
||||
+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ struct vc_msg msg;
|
||||
+ int result;
|
||||
+ uint temp = 0;
|
||||
+ int index = ((struct sensor_device_attribute*)to_sensor_dev_attr(attr))->index;
|
||||
+
|
||||
+ print_debug("IN");
|
||||
+
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&msg, 0, sizeof msg);
|
||||
+
|
||||
+ /* determine the message type */
|
||||
+ if(index == TEMP)
|
||||
+ msg.tag.tag_id = VC_TAG_GET_TEMP;
|
||||
+ else if (index == MAX_TEMP)
|
||||
+ msg.tag.tag_id = VC_TAG_GET_MAX_TEMP;
|
||||
+ else
|
||||
+ {
|
||||
+ print_debug("Unknown temperature message!");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ msg.msg_size = sizeof msg;
|
||||
+ msg.tag.buffer_size = 8;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ result = bcm_mailbox_property(&msg, sizeof msg);
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in milli degrees C */
|
||||
+ if (result == 0 && (msg.request_code & 0x80000000))
|
||||
+ temp = (uint)msg.tag.val;
|
||||
+ #ifdef HWMON_DEBUG_ENABLE
|
||||
+ else
|
||||
+ print_debug("Failed to get temperature!");
|
||||
+ #endif
|
||||
+ print_debug("Got temperature as %u",temp);
|
||||
+ print_debug("OUT");
|
||||
+ return sprintf(buf, "%u\n", temp);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_hwmon_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ print_debug("IN");
|
||||
+ print_debug("HWMON Driver has been probed!");
|
||||
+
|
||||
+ /* check that the device isn't null!*/
|
||||
+ if(pdev == NULL)
|
||||
+ {
|
||||
+ print_debug("Platform device is empty!");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ /* allocate memory for neccessary data */
|
||||
+ bcm2835_data = kzalloc(sizeof(struct bcm2835_hwmon_data),GFP_KERNEL);
|
||||
+ if(!bcm2835_data)
|
||||
+ {
|
||||
+ print_debug("Unable to allocate memory for hwmon data!");
|
||||
+ err = -ENOMEM;
|
||||
+ goto kzalloc_error;
|
||||
+ }
|
||||
+
|
||||
+ /* create the sysfs files */
|
||||
+ if(sysfs_create_group(&pdev->dev.kobj, &bcm2835_attr_group))
|
||||
+ {
|
||||
+ print_debug("Unable to create sysfs files!");
|
||||
+ err = -EFAULT;
|
||||
+ goto sysfs_error;
|
||||
+ }
|
||||
+
|
||||
+ /* register the hwmon device */
|
||||
+ bcm2835_data->hwmon_dev = hwmon_device_register(&pdev->dev);
|
||||
+ if (IS_ERR(bcm2835_data->hwmon_dev))
|
||||
+ {
|
||||
+ err = PTR_ERR(bcm2835_data->hwmon_dev);
|
||||
+ goto hwmon_error;
|
||||
+ }
|
||||
+ print_debug("OUT");
|
||||
+ return 0;
|
||||
+
|
||||
+ /* error goto's */
|
||||
+ hwmon_error:
|
||||
+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group);
|
||||
+
|
||||
+ sysfs_error:
|
||||
+ kfree(bcm2835_data);
|
||||
+
|
||||
+ kzalloc_error:
|
||||
+
|
||||
+ return err;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_hwmon_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ print_debug("IN");
|
||||
+ hwmon_device_unregister(bcm2835_data->hwmon_dev);
|
||||
+
|
||||
+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group);
|
||||
+ print_debug("OUT");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* Hwmon Driver */
|
||||
+static struct platform_driver bcm2835_hwmon_driver = {
|
||||
+ .probe = bcm2835_hwmon_probe,
|
||||
+ .remove = bcm2835_hwmon_remove,
|
||||
+ .driver = {
|
||||
+ .name = "bcm2835_hwmon",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Dorian Peake");
|
||||
+MODULE_DESCRIPTION("HW Monitor driver for bcm2835 chip");
|
||||
+
|
||||
+module_platform_driver(bcm2835_hwmon_driver);
|
||||
Index: linux-3.10/drivers/hwmon/Kconfig
|
||||
===================================================================
|
||||
--- linux-3.10.orig/drivers/hwmon/Kconfig 2013-07-09 20:25:51.321324159 +0100
|
||||
+++ linux-3.10/drivers/hwmon/Kconfig 2013-07-09 20:25:51.313324108 +0100
|
||||
@@ -1537,6 +1537,16 @@
|
||||
help
|
||||
Support for the A/D converter on MC13783 and MC13892 PMIC.
|
||||
|
||||
+config SENSORS_BCM2835
|
||||
+ depends on THERMAL_BCM2835=n
|
||||
+ tristate "Broadcom BCM2835 HWMON Driver"
|
||||
+ help
|
||||
+ If you say yes here you get support for the hardware
|
||||
+ monitoring features of the BCM2835 Chip
|
||||
+
|
||||
+ This driver can also be built as a module. If so, the module
|
||||
+ will be called bcm2835-hwmon.
|
||||
+
|
||||
if ACPI
|
||||
|
||||
comment "ACPI drivers"
|
||||
Index: linux-3.10/drivers/hwmon/Makefile
|
||||
===================================================================
|
||||
--- linux-3.10.orig/drivers/hwmon/Makefile 2013-07-09 20:25:51.321324159 +0100
|
||||
+++ linux-3.10/drivers/hwmon/Makefile 2013-07-09 20:26:07.361403698 +0100
|
||||
@@ -141,6 +141,7 @@
|
||||
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_GSC) += gsc.o
|
||||
+obj-$(CONFIG_SENSORS_BCM2835) += bcm2835-hwmon.o
|
||||
|
||||
obj-$(CONFIG_PMBUS) += pmbus/
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,452 @@
|
|||
diff -urN linux-3.10/drivers/i2c/busses/i2c-bcm2708.c linux-rpi-3.10.y/drivers/i2c/busses/i2c-bcm2708.c
|
||||
--- linux-3.10/drivers/i2c/busses/i2c-bcm2708.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/i2c/busses/i2c-bcm2708.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -0,0 +1,408 @@
|
||||
+/*
|
||||
+ * Driver for Broadcom BCM2708 BSC Controllers
|
||||
+ *
|
||||
+ * Copyright (C) 2012 Chris Boot & Frank Buss
|
||||
+ *
|
||||
+ * This driver is inspired by:
|
||||
+ * i2c-ocores.c, by Peter Korsgaard <jacmet@sunsite.dk>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License
|
||||
+ * along with this program; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/wait.h>
|
||||
+
|
||||
+/* BSC register offsets */
|
||||
+#define BSC_C 0x00
|
||||
+#define BSC_S 0x04
|
||||
+#define BSC_DLEN 0x08
|
||||
+#define BSC_A 0x0c
|
||||
+#define BSC_FIFO 0x10
|
||||
+#define BSC_DIV 0x14
|
||||
+#define BSC_DEL 0x18
|
||||
+#define BSC_CLKT 0x1c
|
||||
+
|
||||
+/* Bitfields in BSC_C */
|
||||
+#define BSC_C_I2CEN 0x00008000
|
||||
+#define BSC_C_INTR 0x00000400
|
||||
+#define BSC_C_INTT 0x00000200
|
||||
+#define BSC_C_INTD 0x00000100
|
||||
+#define BSC_C_ST 0x00000080
|
||||
+#define BSC_C_CLEAR_1 0x00000020
|
||||
+#define BSC_C_CLEAR_2 0x00000010
|
||||
+#define BSC_C_READ 0x00000001
|
||||
+
|
||||
+/* Bitfields in BSC_S */
|
||||
+#define BSC_S_CLKT 0x00000200
|
||||
+#define BSC_S_ERR 0x00000100
|
||||
+#define BSC_S_RXF 0x00000080
|
||||
+#define BSC_S_TXE 0x00000040
|
||||
+#define BSC_S_RXD 0x00000020
|
||||
+#define BSC_S_TXD 0x00000010
|
||||
+#define BSC_S_RXR 0x00000008
|
||||
+#define BSC_S_TXW 0x00000004
|
||||
+#define BSC_S_DONE 0x00000002
|
||||
+#define BSC_S_TA 0x00000001
|
||||
+
|
||||
+#define I2C_TIMEOUT_MS 150
|
||||
+
|
||||
+#define DRV_NAME "bcm2708_i2c"
|
||||
+
|
||||
+static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE;
|
||||
+module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
+MODULE_PARM_DESC(baudrate, "The I2C baudrate");
|
||||
+
|
||||
+
|
||||
+struct bcm2708_i2c {
|
||||
+ struct i2c_adapter adapter;
|
||||
+
|
||||
+ spinlock_t lock;
|
||||
+ void __iomem *base;
|
||||
+ int irq;
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ struct completion done;
|
||||
+
|
||||
+ struct i2c_msg *msg;
|
||||
+ int pos;
|
||||
+ int nmsgs;
|
||||
+ bool error;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * This function sets the ALT mode on the I2C pins so that we can use them with
|
||||
+ * the BSC hardware.
|
||||
+ *
|
||||
+ * FIXME: This is a hack. Use pinmux / pinctrl.
|
||||
+ */
|
||||
+static void bcm2708_i2c_init_pinmode(int id)
|
||||
+{
|
||||
+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
||||
+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
||||
+
|
||||
+ int pin;
|
||||
+ u32 *gpio = ioremap(0x20200000, SZ_16K);
|
||||
+
|
||||
+ BUG_ON(id != 0 && id != 1);
|
||||
+ /* BSC0 is on GPIO 0 & 1, BSC1 is on GPIO 2 & 3 */
|
||||
+ for (pin = id*2+0; pin <= id*2+1; pin++) {
|
||||
+printk("bcm2708_i2c_init_pinmode(%d,%d)\n", id, pin);
|
||||
+ INP_GPIO(pin); /* set mode to GPIO input first */
|
||||
+ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */
|
||||
+ }
|
||||
+
|
||||
+ iounmap(gpio);
|
||||
+
|
||||
+#undef INP_GPIO
|
||||
+#undef SET_GPIO_ALT
|
||||
+}
|
||||
+
|
||||
+static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg)
|
||||
+{
|
||||
+ return readl(bi->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val)
|
||||
+{
|
||||
+ writel(val, bi->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi)
|
||||
+{
|
||||
+ bcm2708_wr(bi, BSC_C, 0);
|
||||
+ bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE);
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi)
|
||||
+{
|
||||
+ while ((bcm2708_rd(bi, BSC_S) & BSC_S_RXD) && (bi->pos < bi->msg->len))
|
||||
+ bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO);
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi)
|
||||
+{
|
||||
+ while ((bcm2708_rd(bi, BSC_S) & BSC_S_TXD) && (bi->pos < bi->msg->len))
|
||||
+ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
|
||||
+}
|
||||
+
|
||||
+static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi)
|
||||
+{
|
||||
+ unsigned long bus_hz;
|
||||
+ u32 cdiv;
|
||||
+ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1;
|
||||
+
|
||||
+ bus_hz = clk_get_rate(bi->clk);
|
||||
+ cdiv = bus_hz / baudrate;
|
||||
+
|
||||
+ if (bi->msg->flags & I2C_M_RD)
|
||||
+ c |= BSC_C_INTR | BSC_C_READ;
|
||||
+ else
|
||||
+ c |= BSC_C_INTT;
|
||||
+
|
||||
+ bcm2708_wr(bi, BSC_DIV, cdiv);
|
||||
+ bcm2708_wr(bi, BSC_A, bi->msg->addr);
|
||||
+ bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
|
||||
+ bcm2708_wr(bi, BSC_C, c);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id)
|
||||
+{
|
||||
+ struct bcm2708_i2c *bi = dev_id;
|
||||
+ bool handled = true;
|
||||
+ u32 s;
|
||||
+
|
||||
+ spin_lock(&bi->lock);
|
||||
+
|
||||
+ /* we may see camera interrupts on the "other" I2C channel
|
||||
+ Just return if we've not sent anything */
|
||||
+ if (!bi->nmsgs || !bi->msg )
|
||||
+ goto early_exit;
|
||||
+
|
||||
+ s = bcm2708_rd(bi, BSC_S);
|
||||
+
|
||||
+ if (s & (BSC_S_CLKT | BSC_S_ERR)) {
|
||||
+ bcm2708_bsc_reset(bi);
|
||||
+ bi->error = true;
|
||||
+
|
||||
+ /* wake up our bh */
|
||||
+ complete(&bi->done);
|
||||
+ } else if (s & BSC_S_DONE) {
|
||||
+ bi->nmsgs--;
|
||||
+
|
||||
+ if (bi->msg->flags & I2C_M_RD)
|
||||
+ bcm2708_bsc_fifo_drain(bi);
|
||||
+
|
||||
+ bcm2708_bsc_reset(bi);
|
||||
+
|
||||
+ if (bi->nmsgs) {
|
||||
+ /* advance to next message */
|
||||
+ bi->msg++;
|
||||
+ bi->pos = 0;
|
||||
+ bcm2708_bsc_setup(bi);
|
||||
+ } else {
|
||||
+ /* wake up our bh */
|
||||
+ complete(&bi->done);
|
||||
+ }
|
||||
+ } else if (s & BSC_S_TXW) {
|
||||
+ bcm2708_bsc_fifo_fill(bi);
|
||||
+ } else if (s & BSC_S_RXR) {
|
||||
+ bcm2708_bsc_fifo_drain(bi);
|
||||
+ } else {
|
||||
+ handled = false;
|
||||
+ }
|
||||
+
|
||||
+early_exit:
|
||||
+ spin_unlock(&bi->lock);
|
||||
+
|
||||
+ return handled ? IRQ_HANDLED : IRQ_NONE;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
+ struct i2c_msg *msgs, int num)
|
||||
+{
|
||||
+ struct bcm2708_i2c *bi = adap->algo_data;
|
||||
+ unsigned long flags;
|
||||
+ int ret;
|
||||
+
|
||||
+ spin_lock_irqsave(&bi->lock, flags);
|
||||
+
|
||||
+ INIT_COMPLETION(bi->done);
|
||||
+ bi->msg = msgs;
|
||||
+ bi->pos = 0;
|
||||
+ bi->nmsgs = num;
|
||||
+ bi->error = false;
|
||||
+
|
||||
+ spin_unlock_irqrestore(&bi->lock, flags);
|
||||
+
|
||||
+ bcm2708_bsc_setup(bi);
|
||||
+
|
||||
+ ret = wait_for_completion_timeout(&bi->done,
|
||||
+ msecs_to_jiffies(I2C_TIMEOUT_MS));
|
||||
+ if (ret == 0) {
|
||||
+ dev_err(&adap->dev, "transfer timed out\n");
|
||||
+ spin_lock_irqsave(&bi->lock, flags);
|
||||
+ bcm2708_bsc_reset(bi);
|
||||
+ spin_unlock_irqrestore(&bi->lock, flags);
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ return bi->error ? -EIO : num;
|
||||
+}
|
||||
+
|
||||
+static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap)
|
||||
+{
|
||||
+ return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL;
|
||||
+}
|
||||
+
|
||||
+static struct i2c_algorithm bcm2708_i2c_algorithm = {
|
||||
+ .master_xfer = bcm2708_i2c_master_xfer,
|
||||
+ .functionality = bcm2708_i2c_functionality,
|
||||
+};
|
||||
+
|
||||
+static int bcm2708_i2c_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *regs;
|
||||
+ int irq, err = -ENOMEM;
|
||||
+ struct clk *clk;
|
||||
+ struct bcm2708_i2c *bi;
|
||||
+ struct i2c_adapter *adap;
|
||||
+
|
||||
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ if (!regs) {
|
||||
+ dev_err(&pdev->dev, "could not get IO memory\n");
|
||||
+ return -ENXIO;
|
||||
+ }
|
||||
+
|
||||
+ irq = platform_get_irq(pdev, 0);
|
||||
+ if (irq < 0) {
|
||||
+ dev_err(&pdev->dev, "could not get IRQ\n");
|
||||
+ return irq;
|
||||
+ }
|
||||
+
|
||||
+ clk = clk_get(&pdev->dev, NULL);
|
||||
+ if (IS_ERR(clk)) {
|
||||
+ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk));
|
||||
+ return PTR_ERR(clk);
|
||||
+ }
|
||||
+
|
||||
+ bcm2708_i2c_init_pinmode(pdev->id);
|
||||
+
|
||||
+ bi = kzalloc(sizeof(*bi), GFP_KERNEL);
|
||||
+ if (!bi)
|
||||
+ goto out_clk_put;
|
||||
+
|
||||
+ platform_set_drvdata(pdev, bi);
|
||||
+
|
||||
+ adap = &bi->adapter;
|
||||
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC;
|
||||
+ adap->algo = &bcm2708_i2c_algorithm;
|
||||
+ adap->algo_data = bi;
|
||||
+ adap->dev.parent = &pdev->dev;
|
||||
+ adap->nr = pdev->id;
|
||||
+ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
|
||||
+
|
||||
+ switch (pdev->id) {
|
||||
+ case 0:
|
||||
+ adap->class = I2C_CLASS_HWMON;
|
||||
+ break;
|
||||
+ case 1:
|
||||
+ adap->class = I2C_CLASS_DDC;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(&pdev->dev, "can only bind to BSC 0 or 1\n");
|
||||
+ err = -ENXIO;
|
||||
+ goto out_free_bi;
|
||||
+ }
|
||||
+
|
||||
+ spin_lock_init(&bi->lock);
|
||||
+ init_completion(&bi->done);
|
||||
+
|
||||
+ bi->base = ioremap(regs->start, resource_size(regs));
|
||||
+ if (!bi->base) {
|
||||
+ dev_err(&pdev->dev, "could not remap memory\n");
|
||||
+ goto out_free_bi;
|
||||
+ }
|
||||
+
|
||||
+ bi->irq = irq;
|
||||
+ bi->clk = clk;
|
||||
+
|
||||
+ err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED,
|
||||
+ dev_name(&pdev->dev), bi);
|
||||
+ if (err) {
|
||||
+ dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
|
||||
+ goto out_iounmap;
|
||||
+ }
|
||||
+
|
||||
+ bcm2708_bsc_reset(bi);
|
||||
+
|
||||
+ err = i2c_add_numbered_adapter(adap);
|
||||
+ if (err < 0) {
|
||||
+ dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err);
|
||||
+ goto out_free_irq;
|
||||
+ }
|
||||
+
|
||||
+ dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %dk)\n",
|
||||
+ pdev->id, (unsigned long)regs->start, irq, baudrate/1000);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+out_free_irq:
|
||||
+ free_irq(bi->irq, bi);
|
||||
+out_iounmap:
|
||||
+ iounmap(bi->base);
|
||||
+out_free_bi:
|
||||
+ kfree(bi);
|
||||
+out_clk_put:
|
||||
+ clk_put(clk);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2c_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct bcm2708_i2c *bi = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, NULL);
|
||||
+
|
||||
+ i2c_del_adapter(&bi->adapter);
|
||||
+ free_irq(bi->irq, bi);
|
||||
+ iounmap(bi->base);
|
||||
+ clk_disable(bi->clk);
|
||||
+ clk_put(bi->clk);
|
||||
+ kfree(bi);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver bcm2708_i2c_driver = {
|
||||
+ .driver = {
|
||||
+ .name = DRV_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .probe = bcm2708_i2c_probe,
|
||||
+ .remove = bcm2708_i2c_remove,
|
||||
+};
|
||||
+
|
||||
+// module_platform_driver(bcm2708_i2c_driver);
|
||||
+
|
||||
+
|
||||
+static int __init bcm2708_i2c_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&bcm2708_i2c_driver);
|
||||
+}
|
||||
+
|
||||
+static void __exit bcm2708_i2c_exit(void)
|
||||
+{
|
||||
+ platform_driver_unregister(&bcm2708_i2c_driver);
|
||||
+}
|
||||
+
|
||||
+module_init(bcm2708_i2c_init);
|
||||
+module_exit(bcm2708_i2c_exit);
|
||||
+
|
||||
+
|
||||
+
|
||||
+MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708");
|
||||
+MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:" DRV_NAME);
|
||||
diff -urN linux-3.10/drivers/i2c/busses/Kconfig linux-rpi-3.10.y/drivers/i2c/busses/Kconfig
|
||||
--- linux-3.10/drivers/i2c/busses/Kconfig 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/i2c/busses/Kconfig 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -343,6 +343,25 @@
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-bcm2835.
|
||||
|
||||
+config I2C_BCM2708
|
||||
+ tristate "BCM2708 BSC"
|
||||
+ depends on MACH_BCM2708
|
||||
+ help
|
||||
+ Enabling this option will add BSC (Broadcom Serial Controller)
|
||||
+ support for the BCM2708. BSC is a Broadcom proprietary bus compatible
|
||||
+ with I2C/TWI/SMBus.
|
||||
+
|
||||
+config I2C_BCM2708_BAUDRATE
|
||||
+ prompt "BCM2708 I2C baudrate"
|
||||
+ depends on I2C_BCM2708
|
||||
+ int
|
||||
+ default 100000
|
||||
+ help
|
||||
+ Set the I2C baudrate. This will alter the default value. A
|
||||
+ different baudrate can be set by using a module parameter as well. If
|
||||
+ no parameter is provided when loading, this is the value that will be
|
||||
+ used.
|
||||
+
|
||||
config I2C_BLACKFIN_TWI
|
||||
tristate "Blackfin TWI I2C support"
|
||||
depends on BLACKFIN
|
||||
diff -urN linux-3.10/drivers/i2c/busses/Makefile linux-rpi-3.10.y/drivers/i2c/busses/Makefile
|
||||
--- linux-3.10/drivers/i2c/busses/Makefile 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/i2c/busses/Makefile 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -32,6 +32,7 @@
|
||||
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
|
||||
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
||||
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
|
||||
+obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.o
|
||||
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
|
||||
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
|
||||
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
|
|
@ -0,0 +1,12 @@
|
|||
diff -urN linux-3.10/drivers/tty/serial/amba-pl011.c linux-rpi-3.10.y/drivers/tty/serial/amba-pl011.c
|
||||
--- linux-3.10/drivers/tty/serial/amba-pl011.c 2013-06-30 23:13:29.000000000 +0100
|
||||
+++ linux-rpi-3.10.y/drivers/tty/serial/amba-pl011.c 2013-07-06 15:25:50.000000000 +0100
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
static unsigned int get_fifosize_arm(unsigned int periphid)
|
||||
{
|
||||
- unsigned int rev = (periphid >> 20) & 0xf;
|
||||
+ unsigned int rev = 0; //(periphid >> 20) & 0xf;
|
||||
return rev < 3 ? 16 : 32;
|
||||
}
|
||||
|
Loading…
Reference in New Issue