aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/android/Kconfig2
-rw-r--r--drivers/auxdisplay/Kconfig14
-rw-r--r--drivers/auxdisplay/Makefile2
-rw-r--r--drivers/auxdisplay/charlcd.c818
-rw-r--r--drivers/auxdisplay/hd44780.c325
-rw-r--r--drivers/char/hpet.c2
-rw-r--r--drivers/char/mspec.c9
-rw-r--r--drivers/char/tpm/tpm-chip.c19
-rw-r--r--drivers/char/virtio_console.c2
-rw-r--r--drivers/dax/dax.c33
-rw-r--r--drivers/firewire/core-topology.c2
-rw-r--r--drivers/firewire/core.h8
-rw-r--r--drivers/fpga/Kconfig13
-rw-r--r--drivers/fpga/Makefile2
-rw-r--r--drivers/fpga/fpga-bridge.c17
-rw-r--r--drivers/fpga/fpga-mgr.c2
-rw-r--r--drivers/fpga/fpga-region.c8
-rw-r--r--drivers/fpga/ice40-spi.c207
-rw-r--r--drivers/fpga/ts73xx-fpga.c156
-rw-r--r--drivers/fpga/zynq-fpga.c28
-rw-r--r--drivers/gpio/gpiolib.c23
-rw-r--r--drivers/hv/channel.c10
-rw-r--r--drivers/hv/channel_mgmt.c48
-rw-r--r--drivers/hv/connection.c65
-rw-r--r--drivers/hv/hv.c5
-rw-r--r--drivers/hv/hv_balloon.c2
-rw-r--r--drivers/hv/hv_fcopy.c2
-rw-r--r--drivers/hv/hv_kvp.c12
-rw-r--r--drivers/hv/hv_snapshot.c2
-rw-r--r--drivers/hv/hyperv_vmbus.h29
-rw-r--r--drivers/hv/ring_buffer.c22
-rw-r--r--drivers/hv/vmbus_drv.c4
-rw-r--r--drivers/iio/industrialio-core.c15
-rw-r--r--drivers/infiniband/core/ucm.c35
-rw-r--r--drivers/infiniband/core/user_mad.c4
-rw-r--r--drivers/infiniband/core/uverbs_main.c2
-rw-r--r--drivers/infiniband/hw/hfi1/device.c2
-rw-r--r--drivers/input/evdev.c11
-rw-r--r--drivers/input/joydev.c11
-rw-r--r--drivers/input/mousedev.c11
-rw-r--r--drivers/media/cec/cec-core.c16
-rw-r--r--drivers/media/media-devnode.c20
-rw-r--r--drivers/misc/Kconfig9
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/aspeed-lpc-ctrl.c267
-rw-r--r--drivers/misc/panel.c827
-rw-r--r--drivers/mtd/ubi/build.c91
-rw-r--r--drivers/mtd/ubi/vmt.c49
-rw-r--r--drivers/platform/chrome/cros_ec_dev.c31
-rw-r--r--drivers/pps/pps.c123
-rw-r--r--drivers/rapidio/devices/rio_mport_cdev.c24
-rw-r--r--drivers/rapidio/rio-sysfs.c76
-rw-r--r--drivers/rapidio/rio.c3
-rw-r--r--drivers/rapidio/rio.h2
-rw-r--r--drivers/rtc/class.c14
-rw-r--r--drivers/rtc/rtc-core.h10
-rw-r--r--drivers/rtc/rtc-dev.c17
-rw-r--r--drivers/scsi/osd/osd_uld.c56
-rw-r--r--drivers/vme/vme.c469
-rw-r--r--drivers/w1/slaves/Kconfig6
-rw-r--r--drivers/w1/slaves/Makefile1
-rw-r--r--drivers/w1/slaves/w1_ds2438.c390
-rw-r--r--drivers/w1/slaves/w1_ds2760.h10
-rw-r--r--drivers/w1/w1_family.h1
-rw-r--r--drivers/zorro/zorro-driver.c15
-rw-r--r--drivers/zorro/zorro-sysfs.c76
-rw-r--r--drivers/zorro/zorro.c3
-rw-r--r--drivers/zorro/zorro.h3
68 files changed, 3209 insertions, 1385 deletions
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
index a82fc022d34b..832e885349b1 100644
--- a/drivers/android/Kconfig
+++ b/drivers/android/Kconfig
@@ -22,7 +22,7 @@ config ANDROID_BINDER_IPC
config ANDROID_BINDER_DEVICES
string "Android Binder devices"
depends on ANDROID_BINDER_IPC
- default "binder"
+ default "binder,hwbinder"
---help---
Default value for the binder.devices parameter.
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 8a8e403644d6..c317dc515617 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -13,8 +13,22 @@ menuconfig AUXDISPLAY
If you say N, all options in this submenu will be skipped and disabled.
+config CHARLCD
+ tristate "Character LCD core support" if COMPILE_TEST
+
if AUXDISPLAY
+config HD44780
+ tristate "HD44780 Character LCD support"
+ depends on GPIOLIB || COMPILE_TEST
+ select CHARLCD
+ ---help---
+ Enable support for Character LCDs using a HD44780 controller.
+ The LCD is accessible through the /dev/lcd char device (10, 156).
+ This code can either be compiled as a module, or linked into the
+ kernel and started at boot.
+ If you don't understand what all this is about, say N.
+
config KS0108
tristate "KS0108 LCD Controller"
depends on PARPORT_PC
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index cb3dd847713b..fcefa686be0f 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -2,7 +2,9 @@
# Makefile for the kernel auxiliary displays device drivers.
#
+obj-$(CONFIG_CHARLCD) += charlcd.o
obj-$(CONFIG_KS0108) += ks0108.o
obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
+obj-$(CONFIG_HD44780) += hd44780.o
obj-$(CONFIG_HT16K33) += ht16k33.o
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
new file mode 100644
index 000000000000..cfeb049a01ef
--- /dev/null
+++ b/drivers/auxdisplay/charlcd.c
@@ -0,0 +1,818 @@
+/*
+ * Character LCD driver for Linux
+ *
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
+ *
+ * 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.
+ */
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+
+#include <generated/utsrelease.h>
+
+#include <misc/charlcd.h>
+
+#define LCD_MINOR 156
+
+#define DEFAULT_LCD_BWIDTH 40
+#define DEFAULT_LCD_HWIDTH 64
+
+/* Keep the backlight on this many seconds for each flash */
+#define LCD_BL_TEMPO_PERIOD 4
+
+#define LCD_FLAG_B 0x0004 /* Blink on */
+#define LCD_FLAG_C 0x0008 /* Cursor on */
+#define LCD_FLAG_D 0x0010 /* Display on */
+#define LCD_FLAG_F 0x0020 /* Large font mode */
+#define LCD_FLAG_N 0x0040 /* 2-rows mode */
+#define LCD_FLAG_L 0x0080 /* Backlight enabled */
+
+/* LCD commands */
+#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
+
+#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */
+#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */
+
+#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */
+#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */
+#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */
+#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */
+
+#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */
+#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */
+#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */
+
+#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */
+#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */
+#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */
+#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */
+
+#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
+
+#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
+
+#define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
+#define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */
+
+struct charlcd_priv {
+ struct charlcd lcd;
+
+ struct delayed_work bl_work;
+ struct mutex bl_tempo_lock; /* Protects access to bl_tempo */
+ bool bl_tempo;
+
+ bool must_clear;
+
+ /* contains the LCD config state */
+ unsigned long int flags;
+
+ /* Contains the LCD X and Y offset */
+ struct {
+ unsigned long int x;
+ unsigned long int y;
+ } addr;
+
+ /* Current escape sequence and it's length or -1 if outside */
+ struct {
+ char buf[LCD_ESCAPE_LEN + 1];
+ int len;
+ } esc_seq;
+
+ unsigned long long drvdata[0];
+};
+
+#define to_priv(p) container_of(p, struct charlcd_priv, lcd)
+
+/* Device single-open policy control */
+static atomic_t charlcd_available = ATOMIC_INIT(1);
+
+/* sleeps that many milliseconds with a reschedule */
+static void long_sleep(int ms)
+{
+ if (in_interrupt())
+ mdelay(ms);
+ else
+ schedule_timeout_interruptible(msecs_to_jiffies(ms));
+}
+
+/* turn the backlight on or off */
+static void charlcd_backlight(struct charlcd *lcd, int on)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ if (!lcd->ops->backlight)
+ return;
+
+ mutex_lock(&priv->bl_tempo_lock);
+ if (!priv->bl_tempo)
+ lcd->ops->backlight(lcd, on);
+ mutex_unlock(&priv->bl_tempo_lock);
+}
+
+static void charlcd_bl_off(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct charlcd_priv *priv =
+ container_of(dwork, struct charlcd_priv, bl_work);
+
+ mutex_lock(&priv->bl_tempo_lock);
+ if (priv->bl_tempo) {
+ priv->bl_tempo = false;
+ if (!(priv->flags & LCD_FLAG_L))
+ priv->lcd.ops->backlight(&priv->lcd, 0);
+ }
+ mutex_unlock(&priv->bl_tempo_lock);
+}
+
+/* turn the backlight on for a little while */
+void charlcd_poke(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ if (!lcd->ops->backlight)
+ return;
+
+ cancel_delayed_work_sync(&priv->bl_work);
+
+ mutex_lock(&priv->bl_tempo_lock);
+ if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
+ lcd->ops->backlight(lcd, 1);
+ priv->bl_tempo = true;
+ schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
+ mutex_unlock(&priv->bl_tempo_lock);
+}
+EXPORT_SYMBOL_GPL(charlcd_poke);
+
+static void charlcd_gotoxy(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+ unsigned int addr;
+
+ /*
+ * we force the cursor to stay at the end of the
+ * line if it wants to go farther
+ */
+ addr = priv->addr.x < lcd->bwidth ? priv->addr.x & (lcd->hwidth - 1)
+ : lcd->bwidth - 1;
+ if (priv->addr.y & 1)
+ addr += lcd->hwidth;
+ if (priv->addr.y & 2)
+ addr += lcd->bwidth;
+ lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
+}
+
+static void charlcd_home(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ priv->addr.x = 0;
+ priv->addr.y = 0;
+ charlcd_gotoxy(lcd);
+}
+
+static void charlcd_print(struct charlcd *lcd, char c)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ if (priv->addr.x < lcd->bwidth) {
+ if (lcd->char_conv)
+ c = lcd->char_conv[(unsigned char)c];
+ lcd->ops->write_data(lcd, c);
+ priv->addr.x++;
+ }
+ /* prevents the cursor from wrapping onto the next line */
+ if (priv->addr.x == lcd->bwidth)
+ charlcd_gotoxy(lcd);
+}
+
+static void charlcd_clear_fast(struct charlcd *lcd)
+{
+ int pos;
+
+ charlcd_home(lcd);
+
+ if (lcd->ops->clear_fast)
+ lcd->ops->clear_fast(lcd);
+ else
+ for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++)
+ lcd->ops->write_data(lcd, ' ');
+
+ charlcd_home(lcd);
+}
+
+/* clears the display and resets X/Y */
+static void charlcd_clear_display(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
+ priv->addr.x = 0;
+ priv->addr.y = 0;
+ /* we must wait a few milliseconds (15) */
+ long_sleep(15);
+}
+
+static int charlcd_init_display(struct charlcd *lcd)
+{
+ void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
+ struct charlcd_priv *priv = to_priv(lcd);
+ u8 init;
+
+ if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
+ return -EINVAL;
+
+ priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
+ LCD_FLAG_C | LCD_FLAG_B;
+
+ long_sleep(20); /* wait 20 ms after power-up for the paranoid */
+
+ /*
+ * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
+ * the LCD is in 8-bit mode afterwards
+ */
+ init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
+ if (lcd->ifwidth == 4) {
+ init >>= 4;
+ write_cmd_raw = lcd->ops->write_cmd_raw4;
+ } else {
+ write_cmd_raw = lcd->ops->write_cmd;
+ }
+ write_cmd_raw(lcd, init);
+ long_sleep(10);
+ write_cmd_raw(lcd, init);
+ long_sleep(10);
+ write_cmd_raw(lcd, init);
+ long_sleep(10);
+
+ if (lcd->ifwidth == 4) {
+ /* Switch to 4-bit mode, 1 line, small fonts */
+ lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4);
+ long_sleep(10);
+ }
+
+ /* set font height and lines number */
+ lcd->ops->write_cmd(lcd,
+ LCD_CMD_FUNCTION_SET |
+ ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
+ ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
+ long_sleep(10);
+
+ /* display off, cursor off, blink off */
+ lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL);
+ long_sleep(10);
+
+ lcd->ops->write_cmd(lcd,
+ LCD_CMD_DISPLAY_CTRL | /* set display mode */
+ ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
+ ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
+ ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
+
+ charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0);
+
+ long_sleep(10);
+
+ /* entry mode set : increment, cursor shifting */
+ lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+
+ charlcd_clear_display(lcd);
+ return 0;
+}
+
+/*
+ * These are the file operation function for user access to /dev/lcd
+ * This function can also be called from inside the kernel, by
+ * setting file and ppos to NULL.
+ *
+ */
+
+static inline int handle_lcd_special_code(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ /* LCD special codes */
+
+ int processed = 0;
+
+ char *esc = priv->esc_seq.buf + 2;
+ int oldflags = priv->flags;
+
+ /* check for display mode flags */
+ switch (*esc) {
+ case 'D': /* Display ON */
+ priv->flags |= LCD_FLAG_D;
+ processed = 1;
+ break;
+ case 'd': /* Display OFF */
+ priv->flags &= ~LCD_FLAG_D;
+ processed = 1;
+ break;
+ case 'C': /* Cursor ON */
+ priv->flags |= LCD_FLAG_C;
+ processed = 1;
+ break;
+ case 'c': /* Cursor OFF */
+ priv->flags &= ~LCD_FLAG_C;
+ processed = 1;
+ break;
+ case 'B': /* Blink ON */
+ priv->flags |= LCD_FLAG_B;
+ processed = 1;
+ break;
+ case 'b': /* Blink OFF */
+ priv->flags &= ~LCD_FLAG_B;
+ processed = 1;
+ break;
+ case '+': /* Back light ON */
+ priv->flags |= LCD_FLAG_L;
+ processed = 1;
+ break;
+ case '-': /* Back light OFF */
+ priv->flags &= ~LCD_FLAG_L;
+ processed = 1;
+ break;
+ case '*': /* Flash back light */
+ charlcd_poke(lcd);
+ processed = 1;
+ break;
+ case 'f': /* Small Font */
+ priv->flags &= ~LCD_FLAG_F;
+ processed = 1;
+ break;
+ case 'F': /* Large Font */
+ priv->flags |= LCD_FLAG_F;
+ processed = 1;
+ break;
+ case 'n': /* One Line */
+ priv->flags &= ~LCD_FLAG_N;
+ processed = 1;
+ break;
+ case 'N': /* Two Lines */
+ priv->flags |= LCD_FLAG_N;
+ break;
+ case 'l': /* Shift Cursor Left */
+ if (priv->addr.x > 0) {
+ /* back one char if not at end of line */
+ if (priv->addr.x < lcd->bwidth)
+ lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+ priv->addr.x--;
+ }
+ processed = 1;
+ break;
+ case 'r': /* shift cursor right */
+ if (priv->addr.x < lcd->width) {
+ /* allow the cursor to pass the end of the line */
+ if (priv->addr.x < (lcd->bwidth - 1))
+ lcd->ops->write_cmd(lcd,
+ LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
+ priv->addr.x++;
+ }
+ processed = 1;
+ break;
+ case 'L': /* shift display left */
+ lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+ processed = 1;
+ break;
+ case 'R': /* shift display right */
+ lcd->ops->write_cmd(lcd,
+ LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
+ LCD_CMD_SHIFT_RIGHT);
+ processed = 1;
+ break;
+ case 'k': { /* kill end of line */
+ int x;
+
+ for (x = priv->addr.x; x < lcd->bwidth; x++)
+ lcd->ops->write_data(lcd, ' ');
+
+ /* restore cursor position */
+ charlcd_gotoxy(lcd);
+ processed = 1;
+ break;
+ }
+ case 'I': /* reinitialize display */
+ charlcd_init_display(lcd);
+ processed = 1;
+ break;
+ case 'G': {
+ /* Generator : LGcxxxxx...xx; must have <c> between '0'
+ * and '7', representing the numerical ASCII code of the
+ * redefined character, and <xx...xx> a sequence of 16
+ * hex digits representing 8 bytes for each character.
+ * Most LCDs will only use 5 lower bits of the 7 first
+ * bytes.
+ */
+
+ unsigned char cgbytes[8];
+ unsigned char cgaddr;
+ int cgoffset;
+ int shift;
+ char value;
+ int addr;
+
+ if (!strchr(esc, ';'))
+ break;
+
+ esc++;
+
+ cgaddr = *(esc++) - '0';
+ if (cgaddr > 7) {
+ processed = 1;
+ break;
+ }
+
+ cgoffset = 0;
+ shift = 0;
+ value = 0;
+ while (*esc && cgoffset < 8) {
+ shift ^= 4;
+ if (*esc >= '0' && *esc <= '9') {
+ value |= (*esc - '0') << shift;
+ } else if (*esc >= 'A' && *esc <= 'Z') {
+ value |= (*esc - 'A' + 10) << shift;
+ } else if (*esc >= 'a' && *esc <= 'z') {
+ value |= (*esc - 'a' + 10) << shift;
+ } else {
+ esc++;
+ continue;
+ }
+
+ if (shift == 0) {
+ cgbytes[cgoffset++] = value;
+ value = 0;
+ }
+
+ esc++;
+ }
+
+ lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
+ for (addr = 0; addr < cgoffset; addr++)
+ lcd->ops->write_data(lcd, cgbytes[addr]);
+
+ /* ensures that we stop writing to CGRAM */
+ charlcd_gotoxy(lcd);
+ processed = 1;
+ break;
+ }
+ case 'x': /* gotoxy : LxXXX[yYYY]; */
+ case 'y': /* gotoxy : LyYYY[xXXX]; */
+ if (!strchr(esc, ';'))
+ break;
+
+ while (*esc) {
+ if (*esc == 'x') {
+ esc++;
+ if (kstrtoul(esc, 10, &priv->addr.x) < 0)
+ break;
+ } else if (*esc == 'y') {
+ esc++;
+ if (kstrtoul(esc, 10, &priv->addr.y) < 0)
+ break;
+ } else {
+ break;
+ }
+ }
+
+ charlcd_gotoxy(lcd);
+ processed = 1;
+ break;
+ }
+
+ /* TODO: This indent party here got ugly, clean it! */
+ /* Check whether one flag was changed */
+ if (oldflags == priv->flags)
+ return processed;
+
+ /* check whether one of B,C,D flags were changed */
+ if ((oldflags ^ priv->flags) &
+ (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
+ /* set display mode */
+ lcd->ops->write_cmd(lcd,
+ LCD_CMD_DISPLAY_CTRL |
+ ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
+ ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
+ ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
+ /* check whether one of F,N flags was changed */
+ else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
+ lcd->ops->write_cmd(lcd,
+ LCD_CMD_FUNCTION_SET |
+ ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
+ ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
+ /* check whether L flag was changed */
+ else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
+ charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
+
+ return processed;
+}
+
+static void charlcd_write_char(struct charlcd *lcd, char c)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ /* first, we'll test if we're in escape mode */
+ if ((c != '\n') && priv->esc_seq.len >= 0) {
+ /* yes, let's add this char to the buffer */
+ priv->esc_seq.buf[priv->esc_seq.len++] = c;
+ priv->esc_seq.buf[priv->esc_seq.len] = 0;
+ } else {
+ /* aborts any previous escape sequence */
+ priv->esc_seq.len = -1;
+
+ switch (c) {
+ case LCD_ESCAPE_CHAR:
+ /* start of an escape sequence */
+ priv->esc_seq.len = 0;
+ priv->esc_seq.buf[priv->esc_seq.len] = 0;
+ break;
+ case '\b':
+ /* go back one char and clear it */
+ if (priv->addr.x > 0) {
+ /*
+ * check if we're not at the
+ * end of the line
+ */
+ if (priv->addr.x < lcd->bwidth)
+ /* back one char */
+ lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+ priv->addr.x--;
+ }
+ /* replace with a space */
+ lcd->ops->write_data(lcd, ' ');
+ /* back one char again */
+ lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+ break;
+ case '\014':
+ /* quickly clear the display */
+ charlcd_clear_fast(lcd);
+ break;
+ case '\n':
+ /*
+ * flush the remainder of the current line and
+ * go to the beginning of the next line
+ */
+ for (; priv->addr.x < lcd->bwidth; priv->addr.x++)
+ lcd->ops->write_data(lcd, ' ');
+ priv->addr.x = 0;
+ priv->addr.y = (priv->addr.y + 1) % lcd->height;
+ charlcd_gotoxy(lcd);
+ break;
+ case '\r':
+ /* go to the beginning of the same line */
+ priv->addr.x = 0;
+ charlcd_gotoxy(lcd);
+ break;
+ case '\t':
+ /* print a space instead of the tab */
+ charlcd_print(lcd, ' ');
+ break;
+ default:
+ /* simply print this char */
+ charlcd_print(lcd, c);
+ break;
+ }
+ }
+
+ /*
+ * now we'll see if we're in an escape mode and if the current
+ * escape sequence can be understood.
+ */
+ if (priv->esc_seq.len >= 2) {
+ int processed = 0;
+
+ if (!strcmp(priv->esc_seq.buf, "[2J")) {
+ /* clear the display */
+ charlcd_clear_fast(lcd);
+ processed = 1;
+ } else if (!strcmp(priv->esc_seq.buf, "[H")) {
+ /* cursor to home */
+ charlcd_home(lcd);
+ processed = 1;
+ }
+ /* codes starting with ^[[L */
+ else if ((priv->esc_seq.len >= 3) &&
+ (priv->esc_seq.buf[0] == '[') &&
+ (priv->esc_seq.buf[1] == 'L')) {
+ processed = handle_lcd_special_code(lcd);
+ }
+
+ /* LCD special escape codes */
+ /*
+ * flush the escape sequence if it's been processed
+ * or if it is getting too long.
+ */
+ if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN))
+ priv->esc_seq.len = -1;
+ } /* escape codes */
+}
+
+static struct charlcd *the_charlcd;
+
+static ssize_t charlcd_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const char __user *tmp = buf;
+ char c;
+
+ for (; count-- > 0; (*ppos)++, tmp++) {
+ if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+ /*
+ * let's be a little nice with other processes
+ * that need some CPU
+ */
+ schedule();
+
+ if (get_user(c, tmp))
+ return -EFAULT;
+
+ charlcd_write_char(the_charlcd, c);
+ }
+
+ return tmp - buf;
+}
+
+static int charlcd_open(struct inode *inode, struct file *file)
+{
+ struct charlcd_priv *priv = to_priv(the_charlcd);
+
+ if (!atomic_dec_and_test(&charlcd_available))
+ return -EBUSY; /* open only once at a time */
+
+ if (file->f_mode & FMODE_READ) /* device is write-only */
+ return -EPERM;
+
+ if (priv->must_clear) {
+ charlcd_clear_display(&priv->lcd);
+ priv->must_clear = false;
+ }
+ return nonseekable_open(inode, file);
+}
+
+static int charlcd_release(struct inode *inode, struct file *file)
+{
+ atomic_inc(&charlcd_available);
+ return 0;
+}
+
+static const struct file_operations charlcd_fops = {
+ .write = charlcd_write,
+ .open = charlcd_open,
+ .release = charlcd_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice charlcd_dev = {
+ .minor = LCD_MINOR,
+ .name = "lcd",
+ .fops = &charlcd_fops,
+};
+
+static void charlcd_puts(struct charlcd *lcd, const char *s)
+{
+ const char *tmp = s;
+ int count = strlen(s);
+
+ for (; count-- > 0; tmp++) {
+ if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+ /*
+ * let's be a little nice with other processes
+ * that need some CPU
+ */
+ schedule();
+
+ charlcd_write_char(lcd, *tmp);
+ }
+}
+
+/* initialize the LCD driver */
+static int charlcd_init(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+ int ret;
+
+ if (lcd->ops->backlight) {
+ mutex_init(&priv->bl_tempo_lock);
+ INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
+ }
+
+ /*
+ * before this line, we must NOT send anything to the display.
+ * Since charlcd_init_display() needs to write data, we have to
+ * enable mark the LCD initialized just before.
+ */
+ ret = charlcd_init_display(lcd);
+ if (ret)
+ return ret;
+
+ /* display a short message */
+#ifdef CONFIG_PANEL_CHANGE_MESSAGE
+#ifdef CONFIG_PANEL_BOOT_MESSAGE
+ charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
+#endif
+#else
+ charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\n");
+#endif
+ /* clear the display on the next device opening */
+ priv->must_clear = true;
+ charlcd_home(lcd);
+ return 0;
+}
+
+struct charlcd *charlcd_alloc(unsigned int drvdata_size)
+{
+ struct charlcd_priv *priv;
+ struct charlcd *lcd;
+
+ priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ priv->esc_seq.len = -1;
+
+ lcd = &priv->lcd;
+ lcd->ifwidth = 8;
+ lcd->bwidth = DEFAULT_LCD_BWIDTH;
+ lcd->hwidth = DEFAULT_LCD_HWIDTH;
+ lcd->drvdata = priv->drvdata;
+
+ return lcd;
+}
+EXPORT_SYMBOL_GPL(charlcd_alloc);
+
+static int panel_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ struct charlcd *lcd = the_charlcd;
+
+ switch (code) {
+ case SYS_DOWN:
+ charlcd_puts(lcd,
+ "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
+ break;
+ case SYS_HALT:
+ charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
+ break;
+ case SYS_POWER_OFF:
+ charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block panel_notifier = {
+ panel_notify_sys,
+ NULL,
+ 0
+};
+
+int charlcd_register(struct charlcd *lcd)
+{
+ int ret;
+
+ ret = charlcd_init(lcd);
+ if (ret)
+ return ret;
+
+ ret = misc_register(&charlcd_dev);
+ if (ret)
+ return ret;
+
+ the_charlcd = lcd;
+ register_reboot_notifier(&panel_notifier);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(charlcd_register);
+
+int charlcd_unregister(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = to_priv(lcd);
+
+ unregister_reboot_notifier(&panel_notifier);
+ charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
+ misc_deregister(&charlcd_dev);
+ the_charlcd = NULL;
+ if (lcd->ops->backlight) {
+ cancel_delayed_work_sync(&priv->bl_work);
+ priv->lcd.ops->backlight(&priv->lcd, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(charlcd_unregister);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
new file mode 100644
index 000000000000..1665ac6ef9ff
--- /dev/null
+++ b/drivers/auxdisplay/hd44780.c
@@ -0,0 +1,325 @@
+/*
+ * HD44780 Character LCD driver for Linux
+ *
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+#include <misc/charlcd.h>
+
+
+enum hd44780_pin {
+ /* Order does matter due to writing to GPIO array subsets! */
+ PIN_DATA0, /* Optional */
+ PIN_DATA1, /* Optional */
+ PIN_DATA2, /* Optional */
+ PIN_DATA3, /* Optional */
+ PIN_DATA4,
+ PIN_DATA5,
+ PIN_DATA6,
+ PIN_DATA7,
+ PIN_CTRL_RS,
+ PIN_CTRL_RW, /* Optional */
+ PIN_CTRL_E,
+ PIN_CTRL_BL, /* Optional */
+ PIN_NUM
+};
+
+struct hd44780 {
+ struct gpio_desc *pins[PIN_NUM];
+};
+
+static void hd44780_backlight(struct charlcd *lcd, int on)
+{
+ struct hd44780 *hd = lcd->drvdata;
+
+ if (hd->pins[PIN_CTRL_BL])
+ gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on);
+}
+
+static void hd44780_strobe_gpio(struct hd44780 *hd)
+{
+ /* Maintain the data during 20 us before the strobe */
+ udelay(20);
+
+ gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 1);
+
+ /* Maintain the strobe during 40 us */
+ udelay(40);
+
+ gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 0);
+}
+
+/* write to an LCD panel register in 8 bit GPIO mode */
+static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
+{
+ int values[10]; /* for DATA[0-7], RS, RW */
+ unsigned int i, n;
+
+ for (i = 0; i < 8; i++)
+ values[PIN_DATA0 + i] = !!(val & BIT(i));
+ values[PIN_CTRL_RS] = rs;
+ n = 9;
+ if (hd->pins[PIN_CTRL_RW]) {
+ values[PIN_CTRL_RW] = 0;
+ n++;
+ }
+
+ /* Present the data to the port */
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+
+ hd44780_strobe_gpio(hd);
+}
+
+/* write to an LCD panel register in 4 bit GPIO mode */
+static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
+{
+ int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ unsigned int i, n;
+
+ /* High nibble + RS, RW */
+ for (i = 4; i < 8; i++)
+ values[PIN_DATA0 + i] = !!(val & BIT(i));
+ values[PIN_CTRL_RS] = rs;
+ n = 5;
+ if (hd->pins[PIN_CTRL_RW]) {
+ values[PIN_CTRL_RW] = 0;
+ n++;
+ }
+
+ /* Present the data to the port */
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+ &values[PIN_DATA4]);
+
+ hd44780_strobe_gpio(hd);
+
+ /* Low nibble */
+ for (i = 0; i < 4; i++)
+ values[PIN_DATA4 + i] = !!(val & BIT(i));
+
+ /* Present the data to the port */
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+ &values[PIN_DATA4]);
+
+ hd44780_strobe_gpio(hd);
+}
+
+/* Send a command to the LCD panel in 8 bit GPIO mode */
+static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
+{
+ struct hd44780 *hd = lcd->drvdata;
+
+ hd44780_write_gpio8(hd, cmd, 0);
+
+ /* The shortest command takes at least 120 us */
+ udelay(120);
+}
+
+/* Send data to the LCD panel in 8 bit GPIO mode */
+static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
+{
+ struct hd44780 *hd = lcd->drvdata;
+
+ hd44780_write_gpio8(hd, data, 1);
+
+ /* The shortest data takes at least 45 us */
+ udelay(45);
+}
+
+static const struct charlcd_ops hd44780_ops_gpio8 = {
+ .write_cmd = hd44780_write_cmd_gpio8,
+ .write_data = hd44780_write_data_gpio8,
+ .backlight = hd44780_backlight,
+};
+
+/* Send a command to the LCD panel in 4 bit GPIO mode */
+static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
+{
+ struct hd44780 *hd = lcd->drvdata;
+
+ hd44780_write_gpio4(hd, cmd, 0);
+
+ /* The shortest command takes at least 120 us */
+ udelay(120);
+}
+
+/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
+static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
+{
+ int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ struct hd44780 *hd = lcd->drvdata;
+ unsigned int i, n;
+
+ /* Command nibble + RS, RW */
+ for (i = 0; i < 4; i++)
+ values[PIN_DATA4 + i] = !!(cmd & BIT(i));
+ values[PIN_CTRL_RS] = 0;
+ n = 5;
+ if (hd->pins[PIN_CTRL_RW]) {
+ values[PIN_CTRL_RW] = 0;
+ n++;
+ }
+
+ /* Present the data to the port */
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+ &values[PIN_DATA4]);
+
+ hd44780_strobe_gpio(hd);
+}
+
+/* Send data to the LCD panel in 4 bit GPIO mode */
+static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
+{
+ struct hd44780 *hd = lcd->drvdata;
+
+ hd44780_write_gpio4(hd, data, 1);
+
+ /* The shortest data takes at least 45 us */
+ udelay(45);
+}
+
+static const struct charlcd_ops hd44780_ops_gpio4 = {
+ .write_cmd = hd44780_write_cmd_gpio4,
+ .write_cmd_raw4 = hd44780_write_cmd_raw_gpio4,
+ .write_data = hd44780_write_data_gpio4,
+ .backlight = hd44780_backlight,
+};
+
+static int hd44780_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned int i, base;
+ struct charlcd *lcd;
+ struct hd44780 *hd;
+ int ifwidth, ret;
+
+ /* Required pins */
+ ifwidth = gpiod_count(dev, "data");
+ if (ifwidth < 0)
+ return ifwidth;
+
+ switch (ifwidth) {
+ case 4:
+ base = PIN_DATA4;
+ break;
+ case 8:
+ base = PIN_DATA0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ lcd = charlcd_alloc(sizeof(struct hd44780));
+ if (!lcd)
+ return -ENOMEM;
+
+ hd = lcd->drvdata;
+
+ for (i = 0; i < ifwidth; i++) {
+ hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(hd->pins[base + i])) {
+ ret = PTR_ERR(hd->pins[base + i]);
+ goto fail;
+ }
+ }
+
+ hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(hd->pins[PIN_CTRL_E])) {
+ ret = PTR_ERR(hd->pins[PIN_CTRL_E]);
+ goto fail;
+ }
+
+ hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, "rs", GPIOD_OUT_HIGH);
+ if (IS_ERR(hd->pins[PIN_CTRL_RS])) {
+ ret = PTR_ERR(hd->pins[PIN_CTRL_RS]);
+ goto fail;
+ }
+
+ /* Optional pins */
+ hd->pins[PIN_CTRL_RW] = devm_gpiod_get_optional(dev, "rw",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(hd->pins[PIN_CTRL_RW])) {
+ ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);
+ goto fail;
+ }
+
+ hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, "backlight",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(hd->pins[PIN_CTRL_BL])) {
+ ret = PTR_ERR(hd->pins[PIN_CTRL_BL]);
+ goto fail;
+ }
+
+ /* Required properties */
+ ret = device_property_read_u32(dev, "display-height", &lcd->height);
+ if (ret)
+ goto fail;
+ ret = device_property_read_u32(dev, "display-width", &lcd->width);
+ if (ret)
+ goto fail;
+
+ /*
+ * On displays with more than two rows, the internal buffer width is
+ * usually equal to the display width
+ */
+ if (lcd->height > 2)
+ lcd->bwidth = lcd->width;
+
+ /* Optional properties */
+ device_property_read_u32(dev, "internal-buffer-width", &lcd->bwidth);
+
+ lcd->ifwidth = ifwidth;
+ lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
+
+ ret = charlcd_register(lcd);
+ if (ret)
+ goto fail;
+
+ platform_set_drvdata(pdev, lcd);
+ return 0;
+
+fail:
+ kfree(lcd);
+ return ret;
+}
+
+static int hd44780_remove(struct platform_device *pdev)
+{
+ struct charlcd *lcd = platform_get_drvdata(pdev);
+
+ charlcd_unregister(lcd);
+ return 0;
+}
+
+static const struct of_device_id hd44780_of_match[] = {
+ { .compatible = "hit,hd44780" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, hd44780_of_match);
+
+static struct platform_driver hd44780_driver = {
+ .probe = hd44780_probe,
+ .remove = hd44780_remove,
+ .driver = {
+ .name = "hd44780",
+ .of_match_table = hd44780_of_match,
+ },
+};
+
+module_platform_driver(hd44780_driver);
+MODULE_DESCRIPTION("HD44780 Character LCD driver");
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 8bdc38d81adf..b941e6d59fd6 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -575,7 +575,7 @@ static inline unsigned long hpet_time_div(struct hpets *hpets,
}
static int
-hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
+hpet_ioctl_common(struct hpet_dev *devp, unsigned int cmd, unsigned long arg,
struct hpet_info *info)
{
struct hpet_timer __iomem *timer;
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index a9c2fa3c81e5..7b75669d3670 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -43,6 +43,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/numa.h>
+#include <linux/refcount.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <linux/atomic.h>
@@ -89,7 +90,7 @@ static int is_sn2;
* protect in fork case where multiple tasks share the vma_data.
*/
struct vma_data {
- atomic_t refcnt; /* Number of vmas sharing the data. */
+ refcount_t refcnt; /* Number of vmas sharing the data. */
spinlock_t lock; /* Serialize access to this structure. */
int count; /* Number of pages allocated. */
enum mspec_page_type type; /* Type of pages allocated. */
@@ -144,7 +145,7 @@ mspec_open(struct vm_area_struct *vma)
struct vma_data *vdata;
vdata = vma->vm_private_data;
- atomic_inc(&vdata->refcnt);
+ refcount_inc(&vdata->refcnt);
}
/*
@@ -162,7 +163,7 @@ mspec_close(struct vm_area_struct *vma)
vdata = vma->vm_private_data;
- if (!atomic_dec_and_test(&vdata->refcnt))
+ if (!refcount_dec_and_test(&vdata->refcnt))
return;
last_index = (vdata->vm_end - vdata->vm_start) >> PAGE_SHIFT;
@@ -274,7 +275,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
vdata->vm_end = vma->vm_end;
vdata->type = type;
spin_lock_init(&vdata->lock);
- atomic_set(&vdata->refcnt, 1);
+ refcount_set(&vdata->refcnt, 1);
vma->vm_private_data = vdata;
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index c406343848da..935f0e92ad61 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -187,7 +187,6 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
cdev_init(&chip->cdev, &tpm_fops);
chip->cdev.owner = THIS_MODULE;
- chip->cdev.kobj.parent = &chip->dev.kobj;
return chip;
@@ -230,27 +229,16 @@ static int tpm_add_char_device(struct tpm_chip *chip)
{
int rc;
- rc = cdev_add(&chip->cdev, chip->dev.devt, 1);
+ rc = cdev_device_add(&chip->cdev, &chip->dev);
if (rc) {
dev_err(&chip->dev,
- "unable to cdev_add() %s, major %d, minor %d, err=%d\n",
+ "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n",
dev_name(&chip->dev), MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
return rc;
}
- rc = device_add(&chip->dev);
- if (rc) {
- dev_err(&chip->dev,
- "unable to device_register() %s, major %d, minor %d, err=%d\n",
- dev_name(&chip->dev), MAJOR(chip->dev.devt),
- MINOR(chip->dev.devt), rc);
-
- cdev_del(&chip->cdev);
- return rc;
- }
-
/* Make the chip available. */
mutex_lock(&idr_lock);
idr_replace(&dev_nums_idr, chip, chip->dev_num);
@@ -261,8 +249,7 @@ static int tpm_add_char_device(struct tpm_chip *chip)
static void tpm_del_char_device(struct tpm_chip *chip)
{
- cdev_del(&chip->cdev);
- device_del(&chip->dev);
+ cdev_device_del(&chip->cdev, &chip->dev);
/* Make the chip unavailable. */
mutex_lock(&idr_lock);
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e9b7e0b3cabe..c6f760b3d9d7 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -2302,7 +2302,7 @@ static int __init init(void)
pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
if (!pdrvdata.debugfs_dir)
- pr_warning("Error creating debugfs dir for virtio-ports\n");
+ pr_warn("Error creating debugfs dir for virtio-ports\n");
INIT_LIST_HEAD(&pdrvdata.consoles);
INIT_LIST_HEAD(&pdrvdata.portdevs);
diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c
index 80c6db279ae1..0d1ca246f257 100644
--- a/drivers/dax/dax.c
+++ b/drivers/dax/dax.c
@@ -702,13 +702,8 @@ static void dax_dev_release(struct device *dev)
kfree(dax_dev);
}
-static void unregister_dax_dev(void *dev)
+static void kill_dax_dev(struct dax_dev *dax_dev)
{
- struct dax_dev *dax_dev = to_dax_dev(dev);
- struct cdev *cdev = &dax_dev->cdev;
-
- dev_dbg(dev, "%s\n", __func__);
-
/*
* Note, rcu is not protecting the liveness of dax_dev, rcu is
* ensuring that any fault handlers that might have seen
@@ -719,8 +714,17 @@ static void unregister_dax_dev(void *dev)
dax_dev->alive = false;
synchronize_rcu();
unmap_mapping_range(dax_dev->inode->i_mapping, 0, 0, 1);
- cdev_del(cdev);
- device_unregister(dev);
+}
+
+static void unregister_dax_dev(void *dev)
+{
+ struct dax_dev *dax_dev = to_dax_dev(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ kill_dax_dev(dax_dev);
+ cdev_device_del(&dax_dev->cdev, dev);
+ put_device(dev);
}
struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
@@ -771,18 +775,13 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
goto err_inode;
}
- /* device_initialize() so cdev can reference kobj parent */
+ /* from here on we're committed to teardown via dax_dev_release() */
device_initialize(dev);
cdev = &dax_dev->cdev;
cdev_init(cdev, &dax_fops);
cdev->owner = parent->driver->owner;
- cdev->kobj.parent = &dev->kobj;
- rc = cdev_add(&dax_dev->cdev, dev_t, 1);
- if (rc)
- goto err_cdev;
- /* from here on we're committed to teardown via dax_dev_release() */
dax_dev->num_resources = count;
dax_dev->alive = true;
dax_dev->region = dax_region;
@@ -794,8 +793,10 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
dev->groups = dax_attribute_groups;
dev->release = dax_dev_release;
dev_set_name(dev, "dax%d.%d", dax_region->id, dax_dev->id);
- rc = device_add(dev);
+
+ rc = cdev_device_add(cdev, dev);
if (rc) {
+ kill_dax_dev(dax_dev);
put_device(dev);
return ERR_PTR(rc);
}
@@ -806,8 +807,6 @@ struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
return dax_dev;
- err_cdev:
- iput(dax_dev->inode);
err_inode:
ida_simple_remove(&dax_minor_ida, minor);
err_minor:
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index 0de83508f321..939d259ddf19 100644
--- a/drivers/firewire/core-topology.c
+++ b/drivers/firewire/core-topology.c
@@ -124,7 +124,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color)
node->initiated_reset = SELF_ID_PHY_INITIATOR(sid);
node->port_count = port_count;
- atomic_set(&node->ref_count, 1);
+ refcount_set(&node->ref_count, 1);
INIT_LIST_HEAD(&node->link);
return node;
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index e1480ff683d2..c07962ead5e4 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -12,7 +12,7 @@
#include <linux/slab.h>
#include <linux/types.h>
-#include <linux/atomic.h>
+#include <linux/refcount.h>
struct device;
struct fw_card;
@@ -184,7 +184,7 @@ struct fw_node {
* local node to this node. */
u8 max_depth:4; /* Maximum depth to any leaf node */
u8 max_hops:4; /* Max hops in this sub tree */
- atomic_t ref_count;
+ refcount_t ref_count;
/* For serializing node topology into a list. */
struct list_head link;
@@ -197,14 +197,14 @@ struct fw_node {
static inline struct fw_node *fw_node_get(struct fw_node *node)
{
- atomic_inc(&node->ref_count);
+ refcount_inc(&node->ref_count);
return node;
}
static inline void fw_node_put(struct fw_node *node)
{
- if (atomic_dec_and_test(&node->ref_count))
+ if (refcount_dec_and_test(&node->ref_count))
kfree(node);
}
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index ce861a2853a4..dee470f9113f 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -20,6 +20,12 @@ config FPGA_REGION
FPGA Regions allow loading FPGA images under control of
the Device Tree.
+config FPGA_MGR_ICE40_SPI
+ tristate "Lattice iCE40 SPI"
+ depends on OF && SPI
+ help
+ FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
+
config FPGA_MGR_SOCFPGA
tristate "Altera SOCFPGA FPGA Manager"
depends on ARCH_SOCFPGA || COMPILE_TEST
@@ -33,6 +39,13 @@ config FPGA_MGR_SOCFPGA_A10
help
FPGA manager driver support for Altera Arria10 SoCFPGA.
+config FPGA_MGR_TS73XX
+ tristate "Technologic Systems TS-73xx SBC FPGA Manager"
+ depends on ARCH_EP93XX && MACH_TS72XX
+ help
+ FPGA manager driver support for the Altera Cyclone II FPGA
+ present on the TS-73xx SBC boards.
+
config FPGA_MGR_ZYNQ_FPGA
tristate "Xilinx Zynq FPGA"
depends on ARCH_ZYNQ || COMPILE_TEST
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 8df07bcf42a6..a5ee3ffe8b18 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -6,8 +6,10 @@
obj-$(CONFIG_FPGA) += fpga-mgr.o
# FPGA Manager Drivers
+obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
+obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
# FPGA Bridge Drivers
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
index 33ee83e6373c..9651aa56244a 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -27,7 +27,7 @@ static DEFINE_IDA(fpga_bridge_ida);
static struct class *fpga_bridge_class;
/* Lock for adding/removing bridges to linked lists*/
-spinlock_t bridge_list_lock;
+static spinlock_t bridge_list_lock;
static int fpga_bridge_of_node_match(struct device *dev, const void *data)
{
@@ -146,11 +146,9 @@ EXPORT_SYMBOL_GPL(fpga_bridge_put);
int fpga_bridges_enable(struct list_head *bridge_list)
{
struct fpga_bridge *bridge;
- struct list_head *node;
int ret;
- list_for_each(node, bridge_list) {
- bridge = list_entry(node, struct fpga_bridge, node);
+ list_for_each_entry(bridge, bridge_list, node) {
ret = fpga_bridge_enable(bridge);
if (ret)
return ret;
@@ -172,11 +170,9 @@ EXPORT_SYMBOL_GPL(fpga_bridges_enable);
int fpga_bridges_disable(struct list_head *bridge_list)
{
struct fpga_bridge *bridge;
- struct list_head *node;
int ret;
- list_for_each(node, bridge_list) {
- bridge = list_entry(node, struct fpga_bridge, node);
+ list_for_each_entry(bridge, bridge_list, node) {
ret = fpga_bridge_disable(bridge);
if (ret)
return ret;
@@ -196,13 +192,10 @@ EXPORT_SYMBOL_GPL(fpga_bridges_disable);
*/
void fpga_bridges_put(struct list_head *bridge_list)
{
- struct fpga_bridge *bridge;
- struct list_head *node, *next;
+ struct fpga_bridge *bridge, *next;
unsigned long flags;
- list_for_each_safe(node, next, bridge_list) {
- bridge = list_entry(node, struct fpga_bridge, node);
-
+ list_for_each_entry_safe(bridge, next, bridge_list, node) {
fpga_bridge_put(bridge);
spin_lock_irqsave(&bridge_list_lock, flags);
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index 86d2cb203533..188ffefa3cc3 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -361,7 +361,7 @@ static struct attribute *fpga_mgr_attrs[] = {
};
ATTRIBUTE_GROUPS(fpga_mgr);
-struct fpga_manager *__fpga_mgr_get(struct device *dev)
+static struct fpga_manager *__fpga_mgr_get(struct device *dev)
{
struct fpga_manager *mgr;
int ret = -ENODEV;
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index 3222fdbad75a..2fe2a52c66ca 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -337,8 +337,9 @@ static int child_regions_with_firmware(struct device_node *overlay)
* The overlay must add either firmware-name or external-fpga-config property
* to the FPGA Region.
*
- * firmware-name : program the FPGA
- * external-fpga-config : FPGA is already programmed
+ * firmware-name : program the FPGA
+ * external-fpga-config : FPGA is already programmed
+ * encrypted-fpga-config : FPGA bitstream is encrypted
*
* The overlay can add other FPGA regions, but child FPGA regions cannot have a
* firmware-name property since those regions don't exist yet.
@@ -373,6 +374,9 @@ static int fpga_region_notify_pre_apply(struct fpga_region *region,
if (of_property_read_bool(nd->overlay, "external-fpga-config"))
info->flags |= FPGA_MGR_EXTERNAL_CONFIG;
+ if (of_property_read_bool(nd->overlay, "encrypted-fpga-config"))
+ info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM;
+
of_property_read_string(nd->overlay, "firmware-name", &firmware_name);
of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us",
diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
new file mode 100644
index 000000000000..7fca82023062
--- /dev/null
+++ b/drivers/fpga/ice40-spi.c
@@ -0,0 +1,207 @@
+/*
+ * FPGA Manager Driver for Lattice iCE40.
+ *
+ * Copyright (c) 2016 Joel Holdsworth
+ *
+ * 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; version 2 of the License.
+ *
+ * This driver adds support to the FPGA manager for configuring the SRAM of
+ * Lattice iCE40 FPGAs through slave SPI.
+ */
+
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/stringify.h>
+
+#define ICE40_SPI_MAX_SPEED 25000000 /* Hz */
+#define ICE40_SPI_MIN_SPEED 1000000 /* Hz */
+
+#define ICE40_SPI_RESET_DELAY 1 /* us (>200ns) */
+#define ICE40_SPI_HOUSEKEEPING_DELAY 1200 /* us */
+
+#define ICE40_SPI_NUM_ACTIVATION_BYTES DIV_ROUND_UP(49, 8)
+
+struct ice40_fpga_priv {
+ struct spi_device *dev;
+ struct gpio_desc *reset;
+ struct gpio_desc *cdone;
+};
+
+static enum fpga_mgr_states ice40_fpga_ops_state(struct fpga_manager *mgr)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+
+ return gpiod_get_value(priv->cdone) ? FPGA_MGR_STATE_OPERATING :
+ FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int ice40_fpga_ops_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+ struct spi_device *dev = priv->dev;
+ struct spi_message message;
+ struct spi_transfer assert_cs_then_reset_delay = {
+ .cs_change = 1,
+ .delay_usecs = ICE40_SPI_RESET_DELAY
+ };
+ struct spi_transfer housekeeping_delay_then_release_cs = {
+ .delay_usecs = ICE40_SPI_HOUSEKEEPING_DELAY
+ };
+ int ret;
+
+ if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ dev_err(&dev->dev,
+ "Partial reconfiguration is not supported\n");
+ return -ENOTSUPP;
+ }
+
+ /* Lock the bus, assert CRESET_B and SS_B and delay >200ns */
+ spi_bus_lock(dev->master);
+
+ gpiod_set_value(priv->reset, 1);
+
+ spi_message_init(&message);
+ spi_message_add_tail(&assert_cs_then_reset_delay, &message);
+ ret = spi_sync_locked(dev, &message);
+
+ /* Come out of reset */
+ gpiod_set_value(priv->reset, 0);
+
+ /* Abort if the chip-select failed */
+ if (ret)
+ goto fail;
+
+ /* Check CDONE is de-asserted i.e. the FPGA is reset */
+ if (gpiod_get_value(priv->cdone)) {
+ dev_err(&dev->dev, "Device reset failed, CDONE is asserted\n");
+ ret = -EIO;
+ goto fail;
+ }
+
+ /* Wait for the housekeeping to complete, and release SS_B */
+ spi_message_init(&message);
+ spi_message_add_tail(&housekeeping_delay_then_release_cs, &message);
+ ret = spi_sync_locked(dev, &message);
+
+fail:
+ spi_bus_unlock(dev->master);
+
+ return ret;
+}
+
+static int ice40_fpga_ops_write(struct fpga_manager *mgr,
+ const char *buf, size_t count)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+
+ return spi_write(priv->dev, buf, count);
+}
+
+static int ice40_fpga_ops_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+ struct spi_device *dev = priv->dev;
+ const u8 padding[ICE40_SPI_NUM_ACTIVATION_BYTES] = {0};
+
+ /* Check CDONE is asserted */
+ if (!gpiod_get_value(priv->cdone)) {
+ dev_err(&dev->dev,
+ "CDONE was not asserted after firmware transfer\n");
+ return -EIO;
+ }
+
+ /* Send of zero-padding to activate the firmware */
+ return spi_write(dev, padding, sizeof(padding));
+}
+
+static const struct fpga_manager_ops ice40_fpga_ops = {
+ .state = ice40_fpga_ops_state,
+ .write_init = ice40_fpga_ops_write_init,
+ .write = ice40_fpga_ops_write,
+ .write_complete = ice40_fpga_ops_write_complete,
+};
+
+static int ice40_fpga_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ice40_fpga_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = spi;
+
+ /* Check board setup data. */
+ if (spi->max_speed_hz > ICE40_SPI_MAX_SPEED) {
+ dev_err(dev, "SPI speed is too high, maximum speed is "
+ __stringify(ICE40_SPI_MAX_SPEED) "\n");
+ return -EINVAL;
+ }
+
+ if (spi->max_speed_hz < ICE40_SPI_MIN_SPEED) {
+ dev_err(dev, "SPI speed is too low, minimum speed is "
+ __stringify(ICE40_SPI_MIN_SPEED) "\n");
+ return -EINVAL;
+ }
+
+ if (spi->mode & SPI_CPHA) {
+ dev_err(dev, "Bad SPI mode, CPHA not supported\n");
+ return -EINVAL;
+ }
+
+ /* Set up the GPIOs */
+ priv->cdone = devm_gpiod_get(dev, "cdone", GPIOD_IN);
+ if (IS_ERR(priv->cdone)) {
+ ret = PTR_ERR(priv->cdone);
+ dev_err(dev, "Failed to get CDONE GPIO: %d\n", ret);
+ return ret;
+ }
+
+ priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset)) {
+ ret = PTR_ERR(priv->reset);
+ dev_err(dev, "Failed to get CRESET_B GPIO: %d\n", ret);
+ return ret;
+ }
+
+ /* Register with the FPGA manager */
+ return fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
+ &ice40_fpga_ops, priv);
+}
+
+static int ice40_fpga_remove(struct spi_device *spi)
+{
+ fpga_mgr_unregister(&spi->dev);
+ return 0;
+}
+
+static const struct of_device_id ice40_fpga_of_match[] = {
+ { .compatible = "lattice,ice40-fpga-mgr", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
+
+static struct spi_driver ice40_fpga_driver = {
+ .probe = ice40_fpga_probe,
+ .remove = ice40_fpga_remove,
+ .driver = {
+ .name = "ice40spi",
+ .of_match_table = of_match_ptr(ice40_fpga_of_match),
+ },
+};
+
+module_spi_driver(ice40_fpga_driver);
+
+MODULE_AUTHOR("Joel Holdsworth <joel@airwebreathe.org.uk>");
+MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
new file mode 100644
index 000000000000..f6a96b42e2ca
--- /dev/null
+++ b/drivers/fpga/ts73xx-fpga.c
@@ -0,0 +1,156 @@
+/*
+ * Technologic Systems TS-73xx SBC FPGA loader
+ *
+ * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com>
+ *
+ * FPGA Manager Driver for the on-board Altera Cyclone II FPGA found on
+ * TS-7300, heavily based on load_fpga.c in their vendor tree.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/iopoll.h>
+#include <linux/fpga/fpga-mgr.h>
+
+#define TS73XX_FPGA_DATA_REG 0
+#define TS73XX_FPGA_CONFIG_REG 1
+
+#define TS73XX_FPGA_WRITE_DONE 0x1
+#define TS73XX_FPGA_WRITE_DONE_TIMEOUT 1000 /* us */
+#define TS73XX_FPGA_RESET 0x2
+#define TS73XX_FPGA_RESET_LOW_DELAY 30 /* us */
+#define TS73XX_FPGA_RESET_HIGH_DELAY 80 /* us */
+#define TS73XX_FPGA_LOAD_OK 0x4
+#define TS73XX_FPGA_CONFIG_LOAD 0x8
+
+struct ts73xx_fpga_priv {
+ void __iomem *io_base;
+ struct device *dev;
+};
+
+static enum fpga_mgr_states ts73xx_fpga_state(struct fpga_manager *mgr)
+{
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int ts73xx_fpga_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ struct ts73xx_fpga_priv *priv = mgr->priv;
+
+ /* Reset the FPGA */
+ writeb(0, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+ udelay(TS73XX_FPGA_RESET_LOW_DELAY);
+ writeb(TS73XX_FPGA_RESET, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+ udelay(TS73XX_FPGA_RESET_HIGH_DELAY);
+
+ return 0;
+}
+
+static int ts73xx_fpga_write(struct fpga_manager *mgr, const char *buf,
+ size_t count)
+{
+ struct ts73xx_fpga_priv *priv = mgr->priv;
+ size_t i = 0;
+ int ret;
+ u8 reg;
+
+ while (count--) {
+ ret = readb_poll_timeout(priv->io_base + TS73XX_FPGA_CONFIG_REG,
+ reg, !(reg & TS73XX_FPGA_WRITE_DONE),
+ 1, TS73XX_FPGA_WRITE_DONE_TIMEOUT);
+ if (ret < 0)
+ return ret;
+
+ writeb(buf[i], priv->io_base + TS73XX_FPGA_DATA_REG);
+ i++;
+ }
+
+ return 0;
+}
+
+static int ts73xx_fpga_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct ts73xx_fpga_priv *priv = mgr->priv;
+ u8 reg;
+
+ usleep_range(1000, 2000);
+ reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
+ reg |= TS73XX_FPGA_CONFIG_LOAD;
+ writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+
+ usleep_range(1000, 2000);
+ reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
+ reg &= ~TS73XX_FPGA_CONFIG_LOAD;
+ writeb(reg, priv->io_base + TS73XX_FPGA_CONFIG_REG);
+
+ reg = readb(priv->io_base + TS73XX_FPGA_CONFIG_REG);
+ if ((reg & TS73XX_FPGA_LOAD_OK) != TS73XX_FPGA_LOAD_OK)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static const struct fpga_manager_ops ts73xx_fpga_ops = {
+ .state = ts73xx_fpga_state,
+ .write_init = ts73xx_fpga_write_init,
+ .write = ts73xx_fpga_write,
+ .write_complete = ts73xx_fpga_write_complete,
+};
+
+static int ts73xx_fpga_probe(struct platform_device *pdev)
+{
+ struct device *kdev = &pdev->dev;
+ struct ts73xx_fpga_priv *priv;
+ struct resource *res;
+
+ priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = kdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->io_base = devm_ioremap_resource(kdev, res);
+ if (IS_ERR(priv->io_base)) {
+ dev_err(kdev, "unable to remap registers\n");
+ return PTR_ERR(priv->io_base);
+ }
+
+ return fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
+ &ts73xx_fpga_ops, priv);
+}
+
+static int ts73xx_fpga_remove(struct platform_device *pdev)
+{
+ fpga_mgr_unregister(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver ts73xx_fpga_driver = {
+ .driver = {
+ .name = "ts73xx-fpga-mgr",
+ },
+ .probe = ts73xx_fpga_probe,
+ .remove = ts73xx_fpga_remove,
+};
+module_platform_driver(ts73xx_fpga_driver);
+
+MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
+MODULE_DESCRIPTION("TS-73xx FPGA Manager driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 34cb98139442..70b15b303471 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -72,6 +72,10 @@
#define CTRL_PCAP_PR_MASK BIT(27)
/* Enable PCAP */
#define CTRL_PCAP_MODE_MASK BIT(26)
+/* Lower rate to allow decrypt on the fly */
+#define CTRL_PCAP_RATE_EN_MASK BIT(25)
+/* System booted in secure mode */
+#define CTRL_SEC_EN_MASK BIT(7)
/* Miscellaneous Control Register bit definitions */
/* Internal PCAP loopback */
@@ -266,6 +270,17 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
if (err)
return err;
+ /* check if bitstream is encrypted & and system's still secure */
+ if (info->flags & FPGA_MGR_ENCRYPTED_BITSTREAM) {
+ ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
+ if (!(ctrl & CTRL_SEC_EN_MASK)) {
+ dev_err(&mgr->dev,
+ "System not secure, can't use crypted bitstreams\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ }
+
/* don't globally reset PL if we're doing partial reconfig */
if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
if (!zynq_fpga_has_sync(buf, count)) {
@@ -337,12 +352,19 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
/* set configuration register with following options:
* - enable PCAP interface
- * - set throughput for maximum speed
+ * - set throughput for maximum speed (if bistream not crypted)
* - set CPU in user mode
*/
ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
- zynq_fpga_write(priv, CTRL_OFFSET,
- (CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK | ctrl));
+ if (info->flags & FPGA_MGR_ENCRYPTED_BITSTREAM)
+ zynq_fpga_write(priv, CTRL_OFFSET,
+ (CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK
+ | CTRL_PCAP_RATE_EN_MASK | ctrl));
+ else
+ zynq_fpga_write(priv, CTRL_OFFSET,
+ (CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK
+ | ctrl));
+
/* We expect that the command queue is empty right now. */
status = zynq_fpga_read(priv, STATUS_OFFSET);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 8b4d721d6d63..3ce2a27ee78f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1035,18 +1035,14 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
cdev_init(&gdev->chrdev, &gpio_fileops);
gdev->chrdev.owner = THIS_MODULE;
- gdev->chrdev.kobj.parent = &gdev->dev.kobj;
gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
- status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
- if (status < 0)
- chip_warn(gdev->chip, "failed to add char device %d:%d\n",
- MAJOR(gpio_devt), gdev->id);
- else
- chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
- MAJOR(gpio_devt), gdev->id);
- status = device_add(&gdev->dev);
+
+ status = cdev_device_add(&gdev->chrdev, &gdev->dev);
if (status)
- goto err_remove_chardev;
+ return status;
+
+ chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
+ MAJOR(gpio_devt), gdev->id);
status = gpiochip_sysfs_register(gdev);
if (status)
@@ -1061,9 +1057,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
return 0;
err_remove_device:
- device_del(&gdev->dev);
-err_remove_chardev:
- cdev_del(&gdev->chrdev);
+ cdev_device_del(&gdev->chrdev, &gdev->dev);
return status;
}
@@ -1347,8 +1341,7 @@ void gpiochip_remove(struct gpio_chip *chip)
* be removed, else it will be dangling until the last user is
* gone.
*/
- cdev_del(&gdev->chrdev);
- device_del(&gdev->dev);
+ cdev_device_del(&gdev->chrdev, &gdev->dev);
put_device(&gdev->dev);
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 321b8833fa6f..736ac76d2a6a 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -333,7 +333,7 @@ static int create_gpadl_header(void *kbuffer, u32 size,
* Gpadl is u32 and we are using a pointer which could
* be 64-bit
* This is governed by the guest/host protocol and
- * so the hypervisor gurantees that this is ok.
+ * so the hypervisor guarantees that this is ok.
*/
for (i = 0; i < pfncurr; i++)
gpadl_body->pfn[i] = slow_virt_to_phys(
@@ -380,7 +380,7 @@ nomem:
}
/*
- * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer
+ * vmbus_establish_gpadl - Establish a GPADL for the specified buffer
*
* @channel: a channel
* @kbuffer: from kmalloc or vmalloc
@@ -731,7 +731,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
/* Setup the descriptor */
desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
desc.flags = flags;
- desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */
+ desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */
desc.length8 = (u16)(packetlen_aligned >> 3);
desc.transactionid = requestid;
desc.rangecount = pagecount;
@@ -792,7 +792,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
/* Setup the descriptor */
desc->type = VM_PKT_DATA_USING_GPA_DIRECT;
desc->flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
- desc->dataoffset8 = desc_size >> 3; /* in 8-bytes grandularity */
+ desc->dataoffset8 = desc_size >> 3; /* in 8-bytes granularity */
desc->length8 = (u16)(packetlen_aligned >> 3);
desc->transactionid = requestid;
desc->rangecount = 1;
@@ -842,7 +842,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
/* Setup the descriptor */
desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
- desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */
+ desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */
desc.length8 = (u16)(packetlen_aligned >> 3);
desc.transactionid = requestid;
desc.rangecount = 1;
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index fbcb06352308..735f9363f2e4 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -1080,30 +1080,30 @@ static void vmbus_onversion_response(
}
/* Channel message dispatch table */
-struct vmbus_channel_message_table_entry
- channel_message_table[CHANNELMSG_COUNT] = {
- {CHANNELMSG_INVALID, 0, NULL},
- {CHANNELMSG_OFFERCHANNEL, 0, vmbus_onoffer},
- {CHANNELMSG_RESCIND_CHANNELOFFER, 0, vmbus_onoffer_rescind},
- {CHANNELMSG_REQUESTOFFERS, 0, NULL},
- {CHANNELMSG_ALLOFFERS_DELIVERED, 1, vmbus_onoffers_delivered},
- {CHANNELMSG_OPENCHANNEL, 0, NULL},
- {CHANNELMSG_OPENCHANNEL_RESULT, 1, vmbus_onopen_result},
- {CHANNELMSG_CLOSECHANNEL, 0, NULL},
- {CHANNELMSG_GPADL_HEADER, 0, NULL},
- {CHANNELMSG_GPADL_BODY, 0, NULL},
- {CHANNELMSG_GPADL_CREATED, 1, vmbus_ongpadl_created},
- {CHANNELMSG_GPADL_TEARDOWN, 0, NULL},
- {CHANNELMSG_GPADL_TORNDOWN, 1, vmbus_ongpadl_torndown},
- {CHANNELMSG_RELID_RELEASED, 0, NULL},
- {CHANNELMSG_INITIATE_CONTACT, 0, NULL},
- {CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response},
- {CHANNELMSG_UNLOAD, 0, NULL},
- {CHANNELMSG_UNLOAD_RESPONSE, 1, vmbus_unload_response},
- {CHANNELMSG_18, 0, NULL},
- {CHANNELMSG_19, 0, NULL},
- {CHANNELMSG_20, 0, NULL},
- {CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL},
+const struct vmbus_channel_message_table_entry
+channel_message_table[CHANNELMSG_COUNT] = {
+ { CHANNELMSG_INVALID, 0, NULL },
+ { CHANNELMSG_OFFERCHANNEL, 0, vmbus_onoffer },
+ { CHANNELMSG_RESCIND_CHANNELOFFER, 0, vmbus_onoffer_rescind },
+ { CHANNELMSG_REQUESTOFFERS, 0, NULL },
+ { CHANNELMSG_ALLOFFERS_DELIVERED, 1, vmbus_onoffers_delivered },
+ { CHANNELMSG_OPENCHANNEL, 0, NULL },
+ { CHANNELMSG_OPENCHANNEL_RESULT, 1, vmbus_onopen_result },
+ { CHANNELMSG_CLOSECHANNEL, 0, NULL },
+ { CHANNELMSG_GPADL_HEADER, 0, NULL },
+ { CHANNELMSG_GPADL_BODY, 0, NULL },
+ { CHANNELMSG_GPADL_CREATED, 1, vmbus_ongpadl_created },
+ { CHANNELMSG_GPADL_TEARDOWN, 0, NULL },
+ { CHANNELMSG_GPADL_TORNDOWN, 1, vmbus_ongpadl_torndown },
+ { CHANNELMSG_RELID_RELEASED, 0, NULL },
+ { CHANNELMSG_INITIATE_CONTACT, 0, NULL },
+ { CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response },
+ { CHANNELMSG_UNLOAD, 0, NULL },
+ { CHANNELMSG_UNLOAD_RESPONSE, 1, vmbus_unload_response },
+ { CHANNELMSG_18, 0, NULL },
+ { CHANNELMSG_19, 0, NULL },
+ { CHANNELMSG_20, 0, NULL },
+ { CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL },
};
/*
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index a8366fec1458..fce27fb141cc 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -296,44 +296,47 @@ struct vmbus_channel *relid2channel(u32 relid)
/*
* vmbus_on_event - Process a channel event notification
+ *
+ * For batched channels (default) optimize host to guest signaling
+ * by ensuring:
+ * 1. While reading the channel, we disable interrupts from host.
+ * 2. Ensure that we process all posted messages from the host
+ * before returning from this callback.
+ * 3. Once we return, enable signaling from the host. Once this
+ * state is set we check to see if additional packets are
+ * available to read. In this case we repeat the process.
+ * If this tasklet has been running for a long time
+ * then reschedule ourselves.
*/
void vmbus_on_event(unsigned long data)
{
struct vmbus_channel *channel = (void *) data;
- void (*callback_fn)(void *);
+ unsigned long time_limit = jiffies + 2;
- /*
- * A channel once created is persistent even when there
- * is no driver handling the device. An unloading driver
- * sets the onchannel_callback to NULL on the same CPU
- * as where this interrupt is handled (in an interrupt context).
- * Thus, checking and invoking the driver specific callback takes
- * care of orderly unloading of the driver.
- */
- callback_fn = READ_ONCE(channel->onchannel_callback);
- if (unlikely(callback_fn == NULL))
- return;
-
- (*callback_fn)(channel->channel_callback_context);
-
- if (channel->callback_mode == HV_CALL_BATCHED) {
- /*
- * This callback reads the messages sent by the host.
- * We can optimize host to guest signaling by ensuring:
- * 1. While reading the channel, we disable interrupts from
- * host.
- * 2. Ensure that we process all posted messages from the host
- * before returning from this callback.
- * 3. Once we return, enable signaling from the host. Once this
- * state is set we check to see if additional packets are
- * available to read. In this case we repeat the process.
+ do {
+ void (*callback_fn)(void *);
+
+ /* A channel once created is persistent even when
+ * there is no driver handling the device. An
+ * unloading driver sets the onchannel_callback to NULL.
*/
- if (hv_end_read(&channel->inbound) != 0) {
- hv_begin_read(&channel->inbound);
+ callback_fn = READ_ONCE(channel->onchannel_callback);
+ if (unlikely(callback_fn == NULL))
+ return;
- tasklet_schedule(&channel->callback_event);
- }
- }
+ (*callback_fn)(channel->channel_callback_context);
+
+ if (channel->callback_mode != HV_CALL_BATCHED)
+ return;
+
+ if (likely(hv_end_read(&channel->inbound) == 0))
+ return;
+
+ hv_begin_read(&channel->inbound);
+ } while (likely(time_before(jiffies, time_limit)));
+
+ /* The time limit (2 jiffies) has been reached */
+ tasklet_schedule(&channel->callback_event);
}
/*
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 665a64f1611e..12e7baecb84e 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -254,7 +254,10 @@ int hv_synic_init(unsigned int cpu)
shared_sint.as_uint64 = 0;
shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
shared_sint.masked = false;
- shared_sint.auto_eoi = true;
+ if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED)
+ shared_sint.auto_eoi = false;
+ else
+ shared_sint.auto_eoi = true;
hv_set_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index 5fd03e59cee5..f5728deff893 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -722,8 +722,6 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
5*HZ);
post_status(&dm_device);
}
-
- return;
}
static void hv_online_page(struct page *pg)
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index a5596a642ed0..daa75bd41f86 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -186,8 +186,6 @@ static void fcopy_send_data(struct work_struct *dummy)
}
}
kfree(smsg_out);
-
- return;
}
/*
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index a1adfe2cfb34..e99ff2ddad40 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -69,7 +69,7 @@ static const int fw_versions[] = {
*
* While the request/response protocol is guaranteed by the host, we further
* ensure this by serializing packet processing in this driver - we do not
- * read additional packets from the VMBUs until the current packet is fully
+ * read additional packets from the VMBUS until the current packet is fully
* handled.
*/
@@ -397,7 +397,7 @@ kvp_send_key(struct work_struct *dummy)
* the max lengths specified. We will however, reserve room
* for the string terminating character - in the utf16s_utf8s()
* function we limit the size of the buffer where the converted
- * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee
+ * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to guarantee
* that the strings can be properly terminated!
*/
@@ -483,8 +483,6 @@ kvp_send_key(struct work_struct *dummy)
}
kfree(message);
-
- return;
}
/*
@@ -533,7 +531,7 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
*/
if (error) {
/*
- * Something failed or we have timedout;
+ * Something failed or we have timed out;
* terminate the current host-side iteration.
*/
goto response_done;
@@ -607,8 +605,8 @@ response_done:
* This callback is invoked when we get a KVP message from the host.
* The host ensures that only one KVP transaction can be active at a time.
* KVP implementation in Linux needs to forward the key to a user-mde
- * component to retrive the corresponding value. Consequently, we cannot
- * respond to the host in the conext of this callback. Since the host
+ * component to retrieve the corresponding value. Consequently, we cannot
+ * respond to the host in the context of this callback. Since the host
* guarantees that at most only one transaction can be active at a time,
* we stash away the transaction state in a set of global variables.
*/
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
index e659d1b94a57..6831efd73394 100644
--- a/drivers/hv/hv_snapshot.c
+++ b/drivers/hv/hv_snapshot.c
@@ -212,8 +212,6 @@ static void vss_send_op(void)
}
kfree(vss_msg);
-
- return;
}
static void vss_handle_request(struct work_struct *dummy)
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 884f83bba1ab..6113e915c50e 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -218,8 +218,8 @@ struct hv_per_cpu_context {
struct hv_context {
/* We only support running on top of Hyper-V
- * So at this point this really can only contain the Hyper-V ID
- */
+ * So at this point this really can only contain the Hyper-V ID
+ */
u64 guestid;
void *tsc_page;
@@ -248,14 +248,6 @@ struct hv_context {
extern struct hv_context hv_context;
-struct hv_ring_buffer_debug_info {
- u32 current_interrupt_mask;
- u32 current_read_index;
- u32 current_write_index;
- u32 bytes_avail_toread;
- u32 bytes_avail_towrite;
-};
-
/* Hv Interface */
extern int hv_init(void);
@@ -289,9 +281,6 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
void *buffer, u32 buflen, u32 *buffer_actual_len,
u64 *requestid, bool raw);
-void hv_ringbuffer_get_debuginfo(const struct hv_ring_buffer_info *ring_info,
- struct hv_ring_buffer_debug_info *debug_info);
-
/*
* Maximum channels is determined by the size of the interrupt page
* which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt
@@ -376,7 +365,7 @@ struct vmbus_channel_message_table_entry {
void (*message_handler)(struct vmbus_channel_message_header *msg);
};
-extern struct vmbus_channel_message_table_entry
+extern const struct vmbus_channel_message_table_entry
channel_message_table[CHANNELMSG_COUNT];
@@ -403,17 +392,17 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep);
void vmbus_on_event(unsigned long data);
void vmbus_on_msg_dpc(unsigned long data);
-int hv_kvp_init(struct hv_util_service *);
+int hv_kvp_init(struct hv_util_service *srv);
void hv_kvp_deinit(void);
-void hv_kvp_onchannelcallback(void *);
+void hv_kvp_onchannelcallback(void *context);
-int hv_vss_init(struct hv_util_service *);
+int hv_vss_init(struct hv_util_service *srv);
void hv_vss_deinit(void);
-void hv_vss_onchannelcallback(void *);
+void hv_vss_onchannelcallback(void *context);
-int hv_fcopy_init(struct hv_util_service *);
+int hv_fcopy_init(struct hv_util_service *srv);
void hv_fcopy_deinit(void);
-void hv_fcopy_onchannelcallback(void *);
+void hv_fcopy_onchannelcallback(void *context);
void vmbus_initiate_unload(bool crash);
static inline void hv_poll_channel(struct vmbus_channel *channel,
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index 87799e81af97..cfacca566e3f 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -73,8 +73,6 @@ static void hv_signal_on_write(u32 old_write, struct vmbus_channel *channel)
*/
if (old_write == READ_ONCE(rbi->ring_buffer->read_index))
vmbus_setevent(channel);
-
- return;
}
/* Get the next write location for the specified ring buffer. */
@@ -208,6 +206,7 @@ void hv_ringbuffer_get_debuginfo(const struct hv_ring_buffer_info *ring_info,
ring_info->ring_buffer->interrupt_mask;
}
}
+EXPORT_SYMBOL_GPL(hv_ringbuffer_get_debuginfo);
/* Initialize the ring buffer. */
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
@@ -267,14 +266,13 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
int hv_ringbuffer_write(struct vmbus_channel *channel,
const struct kvec *kv_list, u32 kv_count)
{
- int i = 0;
+ int i;
u32 bytes_avail_towrite;
- u32 totalbytes_towrite = 0;
-
+ u32 totalbytes_towrite = sizeof(u64);
u32 next_write_location;
u32 old_write;
- u64 prev_indices = 0;
- unsigned long flags = 0;
+ u64 prev_indices;
+ unsigned long flags;
struct hv_ring_buffer_info *outring_info = &channel->outbound;
if (channel->rescind)
@@ -283,8 +281,6 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
for (i = 0; i < kv_count; i++)
totalbytes_towrite += kv_list[i].iov_len;
- totalbytes_towrite += sizeof(u64);
-
spin_lock_irqsave(&outring_info->ring_lock, flags);
bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
@@ -341,18 +337,16 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
u64 *requestid, bool raw)
{
u32 bytes_avail_toread;
- u32 next_read_location = 0;
+ u32 next_read_location;
u64 prev_indices = 0;
struct vmpacket_descriptor desc;
u32 offset;
u32 packetlen;
- int ret = 0;
struct hv_ring_buffer_info *inring_info = &channel->inbound;
if (buflen <= 0)
return -EINVAL;
-
*buffer_actual_len = 0;
*requestid = 0;
@@ -363,7 +357,7 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
* No error is set when there is even no header, drivers are
* supposed to analyze buffer_actual_len.
*/
- return ret;
+ return 0;
}
init_cached_read_index(channel);
@@ -408,5 +402,5 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
hv_signal_on_read(channel);
- return ret;
+ return 0;
}
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 8370b9dc6037..0087b49095eb 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -787,8 +787,6 @@ static void vmbus_shutdown(struct device *child_device)
if (drv->shutdown)
drv->shutdown(dev);
-
- return;
}
@@ -855,7 +853,7 @@ void vmbus_on_msg_dpc(unsigned long data)
struct hv_message *msg = (struct hv_message *)page_addr +
VMBUS_MESSAGE_SINT;
struct vmbus_channel_message_header *hdr;
- struct vmbus_channel_message_table_entry *entry;
+ const struct vmbus_channel_message_table_entry *entry;
struct onmessage_work_context *ctx;
u32 message_type = msg->header.message_type;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index d18ded45bedd..26a03c6eeb47 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1720,18 +1720,13 @@ int iio_device_register(struct iio_dev *indio_dev)
cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
indio_dev->chrdev.owner = indio_dev->info->driver_module;
- indio_dev->chrdev.kobj.parent = &indio_dev->dev.kobj;
- ret = cdev_add(&indio_dev->chrdev, indio_dev->dev.devt, 1);
- if (ret < 0)
- goto error_unreg_eventset;
- ret = device_add(&indio_dev->dev);
+ ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
if (ret < 0)
- goto error_cdev_del;
+ goto error_unreg_eventset;
return 0;
-error_cdev_del:
- cdev_del(&indio_dev->chrdev);
+
error_unreg_eventset:
iio_device_unregister_eventset(indio_dev);
error_free_sysfs:
@@ -1752,10 +1747,8 @@ void iio_device_unregister(struct iio_dev *indio_dev)
{
mutex_lock(&indio_dev->info_exist_lock);
- device_del(&indio_dev->dev);
+ cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
- if (indio_dev->chrdev.dev)
- cdev_del(&indio_dev->chrdev);
iio_device_unregister_debugfs(indio_dev);
iio_disable_all_buffers(indio_dev);
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index cc0d51fb06e3..d15efa4be9ab 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -1205,12 +1205,15 @@ static void ib_ucm_release_dev(struct device *dev)
struct ib_ucm_device *ucm_dev;
ucm_dev = container_of(dev, struct ib_ucm_device, dev);
- cdev_del(&ucm_dev->cdev);
+ kfree(ucm_dev);
+}
+
+static void ib_ucm_free_dev(struct ib_ucm_device *ucm_dev)
+{
if (ucm_dev->devnum < IB_UCM_MAX_DEVICES)
clear_bit(ucm_dev->devnum, dev_map);
else
clear_bit(ucm_dev->devnum - IB_UCM_MAX_DEVICES, overflow_map);
- kfree(ucm_dev);
}
static const struct file_operations ucm_fops = {
@@ -1266,7 +1269,9 @@ static void ib_ucm_add_one(struct ib_device *device)
if (!ucm_dev)
return;
+ device_initialize(&ucm_dev->dev);
ucm_dev->ib_dev = device;
+ ucm_dev->dev.release = ib_ucm_release_dev;
devnum = find_first_zero_bit(dev_map, IB_UCM_MAX_DEVICES);
if (devnum >= IB_UCM_MAX_DEVICES) {
@@ -1286,16 +1291,14 @@ static void ib_ucm_add_one(struct ib_device *device)
cdev_init(&ucm_dev->cdev, &ucm_fops);
ucm_dev->cdev.owner = THIS_MODULE;
kobject_set_name(&ucm_dev->cdev.kobj, "ucm%d", ucm_dev->devnum);
- if (cdev_add(&ucm_dev->cdev, base, 1))
- goto err;
ucm_dev->dev.class = &cm_class;
ucm_dev->dev.parent = device->dev.parent;
- ucm_dev->dev.devt = ucm_dev->cdev.dev;
- ucm_dev->dev.release = ib_ucm_release_dev;
+ ucm_dev->dev.devt = base;
+
dev_set_name(&ucm_dev->dev, "ucm%d", ucm_dev->devnum);
- if (device_register(&ucm_dev->dev))
- goto err_cdev;
+ if (cdev_device_add(&ucm_dev->cdev, &ucm_dev->dev))
+ goto err_devnum;
if (device_create_file(&ucm_dev->dev, &dev_attr_ibdev))
goto err_dev;
@@ -1304,15 +1307,11 @@ static void ib_ucm_add_one(struct ib_device *device)
return;
err_dev:
- device_unregister(&ucm_dev->dev);
-err_cdev:
- cdev_del(&ucm_dev->cdev);
- if (ucm_dev->devnum < IB_UCM_MAX_DEVICES)
- clear_bit(devnum, dev_map);
- else
- clear_bit(devnum, overflow_map);
+ cdev_device_del(&ucm_dev->cdev, &ucm_dev->dev);
+err_devnum:
+ ib_ucm_free_dev(ucm_dev);
err:
- kfree(ucm_dev);
+ put_device(&ucm_dev->dev);
return;
}
@@ -1323,7 +1322,9 @@ static void ib_ucm_remove_one(struct ib_device *device, void *client_data)
if (!ucm_dev)
return;
- device_unregister(&ucm_dev->dev);
+ cdev_device_del(&ucm_dev->cdev, &ucm_dev->dev);
+ ib_ucm_free_dev(ucm_dev);
+ put_device(&ucm_dev->dev);
}
static CLASS_ATTR_STRING(abi_version, S_IRUGO,
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index aca7ff7abedc..25071b880c94 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -1183,7 +1183,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
cdev_init(&port->cdev, &umad_fops);
port->cdev.owner = THIS_MODULE;
- port->cdev.kobj.parent = &umad_dev->kobj;
+ cdev_set_parent(&port->cdev, &umad_dev->kobj);
kobject_set_name(&port->cdev.kobj, "umad%d", port->dev_num);
if (cdev_add(&port->cdev, base, 1))
goto err_cdev;
@@ -1202,7 +1202,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
base += IB_UMAD_MAX_PORTS;
cdev_init(&port->sm_cdev, &umad_sm_fops);
port->sm_cdev.owner = THIS_MODULE;
- port->sm_cdev.kobj.parent = &umad_dev->kobj;
+ cdev_set_parent(&port->sm_cdev, &umad_dev->kobj);
kobject_set_name(&port->sm_cdev.kobj, "issm%d", port->dev_num);
if (cdev_add(&port->sm_cdev, base, 1))
goto err_sm_cdev;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 35c788a32e26..f02d7b9cebea 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -1189,7 +1189,7 @@ static void ib_uverbs_add_one(struct ib_device *device)
cdev_init(&uverbs_dev->cdev, NULL);
uverbs_dev->cdev.owner = THIS_MODULE;
uverbs_dev->cdev.ops = device->mmap ? &uverbs_mmap_fops : &uverbs_fops;
- uverbs_dev->cdev.kobj.parent = &uverbs_dev->kobj;
+ cdev_set_parent(&uverbs_dev->cdev, &uverbs_dev->kobj);
kobject_set_name(&uverbs_dev->cdev.kobj, "uverbs%d", uverbs_dev->devnum);
if (cdev_add(&uverbs_dev->cdev, base, 1))
goto err_cdev;
diff --git a/drivers/infiniband/hw/hfi1/device.c b/drivers/infiniband/hw/hfi1/device.c
index bf64b5a7bfd7..bbb6069dec2a 100644
--- a/drivers/infiniband/hw/hfi1/device.c
+++ b/drivers/infiniband/hw/hfi1/device.c
@@ -69,7 +69,7 @@ int hfi1_cdev_init(int minor, const char *name,
cdev_init(cdev, fops);
cdev->owner = THIS_MODULE;
- cdev->kobj.parent = parent;
+ cdev_set_parent(cdev, parent);
kobject_set_name(&cdev->kobj, name);
ret = cdev_add(cdev, dev, 1);
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e9ae3d500a55..925571475005 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -1354,8 +1354,6 @@ static void evdev_cleanup(struct evdev *evdev)
evdev_mark_dead(evdev);
evdev_hangup(evdev);
- cdev_del(&evdev->cdev);
-
/* evdev is marked dead so no one else accesses evdev->open */
if (evdev->open) {
input_flush_device(handle, NULL);
@@ -1416,12 +1414,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
- evdev->cdev.kobj.parent = &evdev->dev.kobj;
- error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
- if (error)
- goto err_unregister_handle;
- error = device_add(&evdev->dev);
+ error = cdev_device_add(&evdev->cdev, &evdev->dev);
if (error)
goto err_cleanup_evdev;
@@ -1429,7 +1423,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
err_cleanup_evdev:
evdev_cleanup(evdev);
- err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
@@ -1442,7 +1435,7 @@ static void evdev_disconnect(struct input_handle *handle)
{
struct evdev *evdev = handle->private;
- device_del(&evdev->dev);
+ cdev_device_del(&evdev->cdev, &evdev->dev);
evdev_cleanup(evdev);
input_free_minor(MINOR(evdev->dev.devt));
input_unregister_handle(handle);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 065e67bf56dd..29d677c714d2 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -742,8 +742,6 @@ static void joydev_cleanup(struct joydev *joydev)
joydev_mark_dead(joydev);
joydev_hangup(joydev);
- cdev_del(&joydev->cdev);
-
/* joydev is marked dead so no one else accesses joydev->open */
if (joydev->open)
input_close_device(handle);
@@ -913,12 +911,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
goto err_free_joydev;
cdev_init(&joydev->cdev, &joydev_fops);
- joydev->cdev.kobj.parent = &joydev->dev.kobj;
- error = cdev_add(&joydev->cdev, joydev->dev.devt, 1);
- if (error)
- goto err_unregister_handle;
- error = device_add(&joydev->dev);
+ error = cdev_device_add(&joydev->cdev, &joydev->dev);
if (error)
goto err_cleanup_joydev;
@@ -926,7 +920,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
err_cleanup_joydev:
joydev_cleanup(joydev);
- err_unregister_handle:
input_unregister_handle(&joydev->handle);
err_free_joydev:
put_device(&joydev->dev);
@@ -939,7 +932,7 @@ static void joydev_disconnect(struct input_handle *handle)
{
struct joydev *joydev = handle->private;
- device_del(&joydev->dev);
+ cdev_device_del(&joydev->cdev, &joydev->dev);
joydev_cleanup(joydev);
input_free_minor(MINOR(joydev->dev.devt));
input_unregister_handle(handle);
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index b604564dec5c..0e0ff84088fd 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -812,8 +812,6 @@ static void mousedev_cleanup(struct mousedev *mousedev)
mousedev_mark_dead(mousedev);
mousedev_hangup(mousedev);
- cdev_del(&mousedev->cdev);
-
/* mousedev is marked dead so no one else accesses mousedev->open */
if (mousedev->open)
input_close_device(handle);
@@ -901,12 +899,8 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
}
cdev_init(&mousedev->cdev, &mousedev_fops);
- mousedev->cdev.kobj.parent = &mousedev->dev.kobj;
- error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1);
- if (error)
- goto err_unregister_handle;
- error = device_add(&mousedev->dev);
+ error = cdev_device_add(&mousedev->cdev, &mousedev->dev);
if (error)
goto err_cleanup_mousedev;
@@ -914,7 +908,6 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
err_cleanup_mousedev:
mousedev_cleanup(mousedev);
- err_unregister_handle:
if (!mixdev)
input_unregister_handle(&mousedev->handle);
err_free_mousedev:
@@ -927,7 +920,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
static void mousedev_destroy(struct mousedev *mousedev)
{
- device_del(&mousedev->dev);
+ cdev_device_del(&mousedev->cdev, &mousedev->dev);
mousedev_cleanup(mousedev);
input_free_minor(MINOR(mousedev->dev.devt));
if (mousedev != mousedev_mix)
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
index 37217e205040..3163e038a364 100644
--- a/drivers/media/cec/cec-core.c
+++ b/drivers/media/cec/cec-core.c
@@ -137,24 +137,17 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
/* Part 2: Initialize and register the character device */
cdev_init(&devnode->cdev, &cec_devnode_fops);
- devnode->cdev.kobj.parent = &devnode->dev.kobj;
devnode->cdev.owner = owner;
- ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
- if (ret < 0) {
- pr_err("%s: cdev_add failed\n", __func__);
+ ret = cdev_device_add(&devnode->cdev, &devnode->dev);
+ if (ret) {
+ pr_err("%s: cdev_device_add failed\n", __func__);
goto clr_bit;
}
- ret = device_add(&devnode->dev);
- if (ret)
- goto cdev_del;
-
devnode->registered = true;
return 0;
-cdev_del:
- cdev_del(&devnode->cdev);
clr_bit:
mutex_lock(&cec_devnode_lock);
clear_bit(devnode->minor, cec_devnode_nums);
@@ -190,8 +183,7 @@ static void cec_devnode_unregister(struct cec_devnode *devnode)
devnode->unregistered = true;
mutex_unlock(&devnode->lock);
- device_del(&devnode->dev);
- cdev_del(&devnode->cdev);
+ cdev_device_del(&devnode->cdev, &devnode->dev);
put_device(&devnode->dev);
}
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index ae46753c90cb..423248f577b6 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -248,31 +248,22 @@ int __must_check media_devnode_register(struct media_device *mdev,
dev_set_name(&devnode->dev, "media%d", devnode->minor);
device_initialize(&devnode->dev);
- /* Part 2: Initialize and register the character device */
+ /* Part 2: Initialize the character device */
cdev_init(&devnode->cdev, &media_devnode_fops);
devnode->cdev.owner = owner;
- devnode->cdev.kobj.parent = &devnode->dev.kobj;
- ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
+ /* Part 3: Add the media and char device */
+ ret = cdev_device_add(&devnode->cdev, &devnode->dev);
if (ret < 0) {
- pr_err("%s: cdev_add failed\n", __func__);
+ pr_err("%s: cdev_device_add failed\n", __func__);
goto cdev_add_error;
}
- /* Part 3: Add the media device */
- ret = device_add(&devnode->dev);
- if (ret < 0) {
- pr_err("%s: device_add failed\n", __func__);
- goto device_add_error;
- }
-
/* Part 4: Activate this minor. The char device can now be used. */
set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
return 0;
-device_add_error:
- cdev_del(&devnode->cdev);
cdev_add_error:
mutex_lock(&media_devnode_lock);
clear_bit(devnode->minor, media_devnode_nums);
@@ -298,9 +289,8 @@ void media_devnode_unregister(struct media_devnode *devnode)
{
mutex_lock(&media_devnode_lock);
/* Delete the cdev on this minor as well */
- cdev_del(&devnode->cdev);
+ cdev_device_del(&devnode->cdev, &devnode->dev);
mutex_unlock(&media_devnode_lock);
- device_del(&devnode->dev);
devnode->media_dev = NULL;
put_device(&devnode->dev);
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c290990d73ed..fb933b0b9297 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -495,6 +495,7 @@ config VEXPRESS_SYSCFG
config PANEL
tristate "Parallel port LCD/Keypad Panel support"
depends on PARPORT
+ select CHARLCD
---help---
Say Y here if you have an HD44780 or KS-0074 LCD connected to your
parallel port. This driver also features 4 and 6-key keypads. The LCD
@@ -771,6 +772,14 @@ config PANEL_BOOT_MESSAGE
endif # PANEL
+config ASPEED_LPC_CTRL
+ depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+ tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
+ ---help---
+ Control Aspeed ast2400/2500 HOST LPC to BMC mappings through
+ ioctl()s, the driver also provides a read/write interface to a BMC ram
+ region where the host LPC read/write region can be buffered.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7a3ea89339b4..4925ea8e1952 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c
new file mode 100644
index 000000000000..c654651a7b6d
--- /dev/null
+++ b/drivers/misc/aspeed-lpc-ctrl.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2017 IBM 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.
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+
+#include <linux/aspeed-lpc-ctrl.h>
+
+#define DEVICE_NAME "aspeed-lpc-ctrl"
+
+#define HICR7 0x8
+#define HICR8 0xc
+
+struct aspeed_lpc_ctrl {
+ struct miscdevice miscdev;
+ struct regmap *regmap;
+ phys_addr_t mem_base;
+ resource_size_t mem_size;
+ u32 pnor_size;
+ u32 pnor_base;
+};
+
+static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file)
+{
+ return container_of(file->private_data, struct aspeed_lpc_ctrl,
+ miscdev);
+}
+
+static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
+ unsigned long vsize = vma->vm_end - vma->vm_start;
+ pgprot_t prot = vma->vm_page_prot;
+
+ if (vma->vm_pgoff + vsize > lpc_ctrl->mem_base + lpc_ctrl->mem_size)
+ return -EINVAL;
+
+ /* ast2400/2500 AHB accesses are not cache coherent */
+ prot = pgprot_noncached(prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ (lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
+ vsize, prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long param)
+{
+ struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
+ void __user *p = (void __user *)param;
+ struct aspeed_lpc_ctrl_mapping map;
+ u32 addr;
+ u32 size;
+ long rc;
+
+ if (copy_from_user(&map, p, sizeof(map)))
+ return -EFAULT;
+
+ if (map.flags != 0)
+ return -EINVAL;
+
+ switch (cmd) {
+ case ASPEED_LPC_CTRL_IOCTL_GET_SIZE:
+ /* The flash windows don't report their size */
+ if (map.window_type != ASPEED_LPC_CTRL_WINDOW_MEMORY)
+ return -EINVAL;
+
+ /* Support more than one window id in the future */
+ if (map.window_id != 0)
+ return -EINVAL;
+
+ map.size = lpc_ctrl->mem_size;
+
+ return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0;
+ case ASPEED_LPC_CTRL_IOCTL_MAP:
+
+ /*
+ * The top half of HICR7 is the MSB of the BMC address of the
+ * mapping.
+ * The bottom half of HICR7 is the MSB of the HOST LPC
+ * firmware space address of the mapping.
+ *
+ * The 1 bits in the top of half of HICR8 represent the bits
+ * (in the requested address) that should be ignored and
+ * replaced with those from the top half of HICR7.
+ * The 1 bits in the bottom half of HICR8 represent the bits
+ * (in the requested address) that should be kept and pass
+ * into the BMC address space.
+ */
+
+ /*
+ * It doesn't make sense to talk about a size or offset with
+ * low 16 bits set. Both HICR7 and HICR8 talk about the top 16
+ * bits of addresses and sizes.
+ */
+
+ if ((map.size & 0x0000ffff) || (map.offset & 0x0000ffff))
+ return -EINVAL;
+
+ /*
+ * Because of the way the masks work in HICR8 offset has to
+ * be a multiple of size.
+ */
+ if (map.offset & (map.size - 1))
+ return -EINVAL;
+
+ if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) {
+ addr = lpc_ctrl->pnor_base;
+ size = lpc_ctrl->pnor_size;
+ } else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) {
+ addr = lpc_ctrl->mem_base;
+ size = lpc_ctrl->mem_size;
+ } else {
+ return -EINVAL;
+ }
+
+ /* Check overflow first! */
+ if (map.offset + map.size < map.offset ||
+ map.offset + map.size > size)
+ return -EINVAL;
+
+ if (map.size == 0 || map.size > size)
+ return -EINVAL;
+
+ addr += map.offset;
+
+ /*
+ * addr (host lpc address) is safe regardless of values. This
+ * simply changes the address the host has to request on its
+ * side of the LPC bus. This cannot impact the hosts own
+ * memory space by surprise as LPC specific accessors are
+ * required. The only strange thing that could be done is
+ * setting the lower 16 bits but the shift takes care of that.
+ */
+
+ rc = regmap_write(lpc_ctrl->regmap, HICR7,
+ (addr | (map.addr >> 16)));
+ if (rc)
+ return rc;
+
+ return regmap_write(lpc_ctrl->regmap, HICR8,
+ (~(map.size - 1)) | ((map.size >> 16) - 1));
+ }
+
+ return -EINVAL;
+}
+
+static const struct file_operations aspeed_lpc_ctrl_fops = {
+ .owner = THIS_MODULE,
+ .mmap = aspeed_lpc_ctrl_mmap,
+ .unlocked_ioctl = aspeed_lpc_ctrl_ioctl,
+};
+
+static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
+{
+ struct aspeed_lpc_ctrl *lpc_ctrl;
+ struct device_node *node;
+ struct resource resm;
+ struct device *dev;
+ int rc;
+
+ dev = &pdev->dev;
+
+ lpc_ctrl = devm_kzalloc(dev, sizeof(*lpc_ctrl), GFP_KERNEL);
+ if (!lpc_ctrl)
+ return -ENOMEM;
+
+ node = of_parse_phandle(dev->of_node, "flash", 0);
+ if (!node) {
+ dev_err(dev, "Didn't find host pnor flash node\n");
+ return -ENODEV;
+ }
+
+ rc = of_address_to_resource(node, 1, &resm);
+ of_node_put(node);
+ if (rc) {
+ dev_err(dev, "Couldn't address to resource for flash\n");
+ return rc;
+ }
+
+ lpc_ctrl->pnor_size = resource_size(&resm);
+ lpc_ctrl->pnor_base = resm.start;
+
+ dev_set_drvdata(&pdev->dev, lpc_ctrl);
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!node) {
+ dev_err(dev, "Didn't find reserved memory\n");
+ return -EINVAL;
+ }
+
+ rc = of_address_to_resource(node, 0, &resm);
+ of_node_put(node);
+ if (rc) {
+ dev_err(dev, "Couldn't address to resource for reserved memory\n");
+ return -ENOMEM;
+ }
+
+ lpc_ctrl->mem_size = resource_size(&resm);
+ lpc_ctrl->mem_base = resm.start;
+
+ lpc_ctrl->regmap = syscon_node_to_regmap(
+ pdev->dev.parent->of_node);
+ if (IS_ERR(lpc_ctrl->regmap)) {
+ dev_err(dev, "Couldn't get regmap\n");
+ return -ENODEV;
+ }
+
+ lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
+ lpc_ctrl->miscdev.name = DEVICE_NAME;
+ lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
+ lpc_ctrl->miscdev.parent = dev;
+ rc = misc_register(&lpc_ctrl->miscdev);
+ if (rc)
+ dev_err(dev, "Unable to register device\n");
+ else
+ dev_info(dev, "Loaded at %pap (0x%08x)\n",
+ &lpc_ctrl->mem_base, lpc_ctrl->mem_size);
+
+ return rc;
+}
+
+static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
+{
+ struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
+
+ misc_deregister(&lpc_ctrl->miscdev);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_lpc_ctrl_match[] = {
+ { .compatible = "aspeed,ast2400-lpc-ctrl" },
+ { .compatible = "aspeed,ast2500-lpc-ctrl" },
+ { },
+};
+
+static struct platform_driver aspeed_lpc_ctrl_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = aspeed_lpc_ctrl_match,
+ },
+ .probe = aspeed_lpc_ctrl_probe,
+ .remove = aspeed_lpc_ctrl_remove,
+};
+
+module_platform_driver(aspeed_lpc_ctrl_driver);
+
+MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
+MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings");
diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index ef2ece0f26af..e0c014c2356f 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -1,6 +1,7 @@
/*
* Front panel driver for Linux
* Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -54,15 +55,12 @@
#include <linux/ctype.h>
#include <linux/parport.h>
#include <linux/list.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <generated/utsrelease.h>
#include <linux/io.h>
#include <linux/uaccess.h>
-#define LCD_MINOR 156
+#include <misc/charlcd.h>
+
#define KEYPAD_MINOR 185
#define LCD_MAXBYTES 256 /* max burst write */
@@ -76,9 +74,6 @@
/* a key repeats this times INPUT_POLL_TIME */
#define KEYPAD_REP_DELAY (2)
-/* keep the light on this many seconds for each flash */
-#define FLASH_LIGHT_TEMPO (4)
-
/* converts an r_str() input to an active high, bits string : 000BAOSE */
#define PNL_PINPUT(a) ((((unsigned char)(a)) ^ 0x7F) >> 3)
@@ -120,40 +115,6 @@
#define PIN_SELECP 17
#define PIN_NOT_SET 127
-#define LCD_FLAG_B 0x0004 /* blink on */
-#define LCD_FLAG_C 0x0008 /* cursor on */
-#define LCD_FLAG_D 0x0010 /* display on */
-#define LCD_FLAG_F 0x0020 /* large font mode */
-#define LCD_FLAG_N 0x0040 /* 2-rows mode */
-#define LCD_FLAG_L 0x0080 /* backlight enabled */
-
-/* LCD commands */
-#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
-
-#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */
-#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */
-
-#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */
-#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */
-#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */
-#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */
-
-#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */
-#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */
-#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */
-
-#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */
-#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */
-#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */
-#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */
-
-#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
-
-#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
-
-#define LCD_ESCAPE_LEN 24 /* max chars for LCD escape command */
-#define LCD_ESCAPE_CHAR 27 /* use char 27 for escape command */
-
#define NOT_SET -1
/* macros to simplify use of the parallel port */
@@ -245,19 +206,10 @@ static wait_queue_head_t keypad_read_wait;
static struct {
bool enabled;
bool initialized;
- bool must_clear;
- int height;
- int width;
- int bwidth;
- int hwidth;
int charset;
int proto;
- struct delayed_work bl_work;
- struct mutex bl_tempo_lock; /* Protects access to bl_tempo */
- bool bl_tempo;
-
/* TODO: use union here? */
struct {
int e;
@@ -268,20 +220,7 @@ static struct {
int bl;
} pins;
- /* contains the LCD config state */
- unsigned long int flags;
-
- /* Contains the LCD X and Y offset */
- struct {
- unsigned long int x;
- unsigned long int y;
- } addr;
-
- /* Current escape sequence and it's length or -1 if outside */
- struct {
- char buf[LCD_ESCAPE_LEN + 1];
- int len;
- } esc_seq;
+ struct charlcd *charlcd;
} lcd;
/* Needed only for init */
@@ -464,17 +403,12 @@ static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES];
/* global variables */
/* Device single-open policy control */
-static atomic_t lcd_available = ATOMIC_INIT(1);
static atomic_t keypad_available = ATOMIC_INIT(1);
static struct pardevice *pprt;
static int keypad_initialized;
-static void (*lcd_write_cmd)(int);
-static void (*lcd_write_data)(int);
-static void (*lcd_clear_fast)(void);
-
static DEFINE_SPINLOCK(pprt_lock);
static struct timer_list scan_timer;
@@ -574,8 +508,6 @@ static int keypad_enabled = NOT_SET;
module_param(keypad_enabled, int, 0000);
MODULE_PARM_DESC(keypad_enabled, "Deprecated option, use keypad_type instead");
-static const unsigned char *lcd_char_conv;
-
/* for some LCD drivers (ks0074) we need a charset conversion table. */
static const unsigned char lcd_char_conv_ks0074[256] = {
/* 0|8 1|9 2|A 3|B 4|C 5|D 6|E 7|F */
@@ -752,15 +684,6 @@ static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val)
}
}
-/* sleeps that many milliseconds with a reschedule */
-static void long_sleep(int ms)
-{
- if (in_interrupt())
- mdelay(ms);
- else
- schedule_timeout_interruptible(msecs_to_jiffies(ms));
-}
-
/*
* send a serial byte to the LCD panel. The caller is responsible for locking
* if needed.
@@ -792,8 +715,11 @@ static void lcd_send_serial(int byte)
}
/* turn the backlight on or off */
-static void __lcd_backlight(int on)
+static void lcd_backlight(struct charlcd *charlcd, int on)
{
+ if (lcd.pins.bl == PIN_NONE)
+ return;
+
/* The backlight is activated by setting the AUTOFEED line to +5V */
spin_lock_irq(&pprt_lock);
if (on)
@@ -804,46 +730,8 @@ static void __lcd_backlight(int on)
spin_unlock_irq(&pprt_lock);
}
-static void lcd_backlight(int on)
-{
- if (lcd.pins.bl == PIN_NONE)
- return;
-
- mutex_lock(&lcd.bl_tempo_lock);
- if (!lcd.bl_tempo)
- __lcd_backlight(on);
- mutex_unlock(&lcd.bl_tempo_lock);
-}
-
-static void lcd_bl_off(struct work_struct *work)
-{
- mutex_lock(&lcd.bl_tempo_lock);
- if (lcd.bl_tempo) {
- lcd.bl_tempo = false;
- if (!(lcd.flags & LCD_FLAG_L))
- __lcd_backlight(0);
- }
- mutex_unlock(&lcd.bl_tempo_lock);
-}
-
-/* turn the backlight on for a little while */
-static void lcd_poke(void)
-{
- if (lcd.pins.bl == PIN_NONE)
- return;
-
- cancel_delayed_work_sync(&lcd.bl_work);
-
- mutex_lock(&lcd.bl_tempo_lock);
- if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L))
- __lcd_backlight(1);
- lcd.bl_tempo = true;
- schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ);
- mutex_unlock(&lcd.bl_tempo_lock);
-}
-
/* send a command to the LCD panel in serial mode */
-static void lcd_write_cmd_s(int cmd)
+static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
{
spin_lock_irq(&pprt_lock);
lcd_send_serial(0x1F); /* R/W=W, RS=0 */
@@ -854,7 +742,7 @@ static void lcd_write_cmd_s(int cmd)
}
/* send data to the LCD panel in serial mode */
-static void lcd_write_data_s(int data)
+static void lcd_write_data_s(struct charlcd *charlcd, int data)
{
spin_lock_irq(&pprt_lock);
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
@@ -865,7 +753,7 @@ static void lcd_write_data_s(int data)
}
/* send a command to the LCD panel in 8 bits parallel mode */
-static void lcd_write_cmd_p8(int cmd)
+static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -887,7 +775,7 @@ static void lcd_write_cmd_p8(int cmd)
}
/* send data to the LCD panel in 8 bits parallel mode */
-static void lcd_write_data_p8(int data)
+static void lcd_write_data_p8(struct charlcd *charlcd, int data)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -909,7 +797,7 @@ static void lcd_write_data_p8(int data)
}
/* send a command to the TI LCD panel */
-static void lcd_write_cmd_tilcd(int cmd)
+static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
{
spin_lock_irq(&pprt_lock);
/* present the data to the control port */
@@ -919,7 +807,7 @@ static void lcd_write_cmd_tilcd(int cmd)
}
/* send data to the TI LCD panel */
-static void lcd_write_data_tilcd(int data)
+static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -928,47 +816,13 @@ static void lcd_write_data_tilcd(int data)
spin_unlock_irq(&pprt_lock);
}
-static void lcd_gotoxy(void)
-{
- lcd_write_cmd(LCD_CMD_SET_DDRAM_ADDR
- | (lcd.addr.y ? lcd.hwidth : 0)
- /*
- * we force the cursor to stay at the end of the
- * line if it wants to go farther
- */
- | ((lcd.addr.x < lcd.bwidth) ? lcd.addr.x &
- (lcd.hwidth - 1) : lcd.bwidth - 1));
-}
-
-static void lcd_home(void)
-{
- lcd.addr.x = 0;
- lcd.addr.y = 0;
- lcd_gotoxy();
-}
-
-static void lcd_print(char c)
-{
- if (lcd.addr.x < lcd.bwidth) {
- if (lcd_char_conv)
- c = lcd_char_conv[(unsigned char)c];
- lcd_write_data(c);
- lcd.addr.x++;
- }
- /* prevents the cursor from wrapping onto the next line */
- if (lcd.addr.x == lcd.bwidth)
- lcd_gotoxy();
-}
-
/* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_s(void)
+static void lcd_clear_fast_s(struct charlcd *charlcd)
{
int pos;
- lcd_home();
-
spin_lock_irq(&pprt_lock);
- for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+ for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
lcd_send_serial(' ' & 0x0F);
lcd_send_serial((' ' >> 4) & 0x0F);
@@ -976,19 +830,15 @@ static void lcd_clear_fast_s(void)
udelay(40);
}
spin_unlock_irq(&pprt_lock);
-
- lcd_home();
}
/* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_p8(void)
+static void lcd_clear_fast_p8(struct charlcd *charlcd)
{
int pos;
- lcd_home();
-
spin_lock_irq(&pprt_lock);
- for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+ for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
/* present the data to the data port */
w_dtr(pprt, ' ');
@@ -1010,488 +860,62 @@ static void lcd_clear_fast_p8(void)
udelay(45);
}
spin_unlock_irq(&pprt_lock);
-
- lcd_home();
}
/* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_tilcd(void)
+static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
{
int pos;
- lcd_home();
-
spin_lock_irq(&pprt_lock);
- for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+ for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
/* present the data to the data port */
w_dtr(pprt, ' ');
udelay(60);
}
spin_unlock_irq(&pprt_lock);
-
- lcd_home();
-}
-
-/* clears the display and resets X/Y */
-static void lcd_clear_display(void)
-{
- lcd_write_cmd(LCD_CMD_DISPLAY_CLEAR);
- lcd.addr.x = 0;
- lcd.addr.y = 0;
- /* we must wait a few milliseconds (15) */
- long_sleep(15);
}
-static void lcd_init_display(void)
-{
- lcd.flags = ((lcd.height > 1) ? LCD_FLAG_N : 0)
- | LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B;
-
- long_sleep(20); /* wait 20 ms after power-up for the paranoid */
-
- /* 8bits, 1 line, small fonts; let's do it 3 times */
- lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
- long_sleep(10);
- lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
- long_sleep(10);
- lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
- long_sleep(10);
-
- /* set font height and lines number */
- lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS
- | ((lcd.flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0)
- | ((lcd.flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)
- );
- long_sleep(10);
-
- /* display off, cursor off, blink off */
- lcd_write_cmd(LCD_CMD_DISPLAY_CTRL);
- long_sleep(10);
-
- lcd_write_cmd(LCD_CMD_DISPLAY_CTRL /* set display mode */
- | ((lcd.flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0)
- | ((lcd.flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0)
- | ((lcd.flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)
- );
-
- lcd_backlight((lcd.flags & LCD_FLAG_L) ? 1 : 0);
-
- long_sleep(10);
-
- /* entry mode set : increment, cursor shifting */
- lcd_write_cmd(LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+static struct charlcd_ops charlcd_serial_ops = {
+ .write_cmd = lcd_write_cmd_s,
+ .write_data = lcd_write_data_s,
+ .clear_fast = lcd_clear_fast_s,
+ .backlight = lcd_backlight,
+};
- lcd_clear_display();
-}
+static struct charlcd_ops charlcd_parallel_ops = {
+ .write_cmd = lcd_write_cmd_p8,
+ .write_data = lcd_write_data_p8,
+ .clear_fast = lcd_clear_fast_p8,
+ .backlight = lcd_backlight,
+};
-/*
- * These are the file operation function for user access to /dev/lcd
- * This function can also be called from inside the kernel, by
- * setting file and ppos to NULL.
- *
- */
+static struct charlcd_ops charlcd_tilcd_ops = {
+ .write_cmd = lcd_write_cmd_tilcd,
+ .write_data = lcd_write_data_tilcd,
+ .clear_fast = lcd_clear_fast_tilcd,
+ .backlight = lcd_backlight,
+};
-static inline int handle_lcd_special_code(void)
+/* initialize the LCD driver */
+static void lcd_init(void)
{
- /* LCD special codes */
-
- int processed = 0;
+ struct charlcd *charlcd;
- char *esc = lcd.esc_seq.buf + 2;
- int oldflags = lcd.flags;
-
- /* check for display mode flags */
- switch (*esc) {
- case 'D': /* Display ON */
- lcd.flags |= LCD_FLAG_D;
- processed = 1;
- break;
- case 'd': /* Display OFF */
- lcd.flags &= ~LCD_FLAG_D;
- processed = 1;
- break;
- case 'C': /* Cursor ON */
- lcd.flags |= LCD_FLAG_C;
- processed = 1;
- break;
- case 'c': /* Cursor OFF */
- lcd.flags &= ~LCD_FLAG_C;
- processed = 1;
- break;
- case 'B': /* Blink ON */
- lcd.flags |= LCD_FLAG_B;
- processed = 1;
- break;
- case 'b': /* Blink OFF */
- lcd.flags &= ~LCD_FLAG_B;
- processed = 1;
- break;
- case '+': /* Back light ON */
- lcd.flags |= LCD_FLAG_L;
- processed = 1;
- break;
- case '-': /* Back light OFF */
- lcd.flags &= ~LCD_FLAG_L;
- processed = 1;
- break;
- case '*':
- /* flash back light */
- lcd_poke();
- processed = 1;
- break;
- case 'f': /* Small Font */
- lcd.flags &= ~LCD_FLAG_F;
- processed = 1;
- break;
- case 'F': /* Large Font */
- lcd.flags |= LCD_FLAG_F;
- processed = 1;
- break;
- case 'n': /* One Line */
- lcd.flags &= ~LCD_FLAG_N;
- processed = 1;
- break;
- case 'N': /* Two Lines */
- lcd.flags |= LCD_FLAG_N;
- break;
- case 'l': /* Shift Cursor Left */
- if (lcd.addr.x > 0) {
- /* back one char if not at end of line */
- if (lcd.addr.x < lcd.bwidth)
- lcd_write_cmd(LCD_CMD_SHIFT);
- lcd.addr.x--;
- }
- processed = 1;
- break;
- case 'r': /* shift cursor right */
- if (lcd.addr.x < lcd.width) {
- /* allow the cursor to pass the end of the line */
- if (lcd.addr.x < (lcd.bwidth - 1))
- lcd_write_cmd(LCD_CMD_SHIFT |
- LCD_CMD_SHIFT_RIGHT);
- lcd.addr.x++;
- }
- processed = 1;
- break;
- case 'L': /* shift display left */
- lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
- processed = 1;
- break;
- case 'R': /* shift display right */
- lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
- LCD_CMD_SHIFT_RIGHT);
- processed = 1;
- break;
- case 'k': { /* kill end of line */
- int x;
-
- for (x = lcd.addr.x; x < lcd.bwidth; x++)
- lcd_write_data(' ');
-
- /* restore cursor position */
- lcd_gotoxy();
- processed = 1;
- break;
- }
- case 'I': /* reinitialize display */
- lcd_init_display();
- processed = 1;
- break;
- case 'G': {
- /* Generator : LGcxxxxx...xx; must have <c> between '0'
- * and '7', representing the numerical ASCII code of the
- * redefined character, and <xx...xx> a sequence of 16
- * hex digits representing 8 bytes for each character.
- * Most LCDs will only use 5 lower bits of the 7 first
- * bytes.
- */
-
- unsigned char cgbytes[8];
- unsigned char cgaddr;
- int cgoffset;
- int shift;
- char value;
- int addr;
-
- if (!strchr(esc, ';'))
- break;
-
- esc++;
-
- cgaddr = *(esc++) - '0';
- if (cgaddr > 7) {
- processed = 1;
- break;
- }
-
- cgoffset = 0;
- shift = 0;
- value = 0;
- while (*esc && cgoffset < 8) {
- shift ^= 4;
- if (*esc >= '0' && *esc <= '9') {
- value |= (*esc - '0') << shift;
- } else if (*esc >= 'A' && *esc <= 'Z') {
- value |= (*esc - 'A' + 10) << shift;
- } else if (*esc >= 'a' && *esc <= 'z') {
- value |= (*esc - 'a' + 10) << shift;
- } else {
- esc++;
- continue;
- }
-
- if (shift == 0) {
- cgbytes[cgoffset++] = value;
- value = 0;
- }
-
- esc++;
- }
-
- lcd_write_cmd(LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
- for (addr = 0; addr < cgoffset; addr++)
- lcd_write_data(cgbytes[addr]);
-
- /* ensures that we stop writing to CGRAM */
- lcd_gotoxy();
- processed = 1;
- break;
- }
- case 'x': /* gotoxy : LxXXX[yYYY]; */
- case 'y': /* gotoxy : LyYYY[xXXX]; */
- if (!strchr(esc, ';'))
- break;
-
- while (*esc) {
- if (*esc == 'x') {
- esc++;
- if (kstrtoul(esc, 10, &lcd.addr.x) < 0)
- break;
- } else if (*esc == 'y') {
- esc++;
- if (kstrtoul(esc, 10, &lcd.addr.y) < 0)
- break;
- } else {
- break;
- }
- }
-
- lcd_gotoxy();
- processed = 1;
- break;
- }
-
- /* TODO: This indent party here got ugly, clean it! */
- /* Check whether one flag was changed */
- if (oldflags != lcd.flags) {
- /* check whether one of B,C,D flags were changed */
- if ((oldflags ^ lcd.flags) &
- (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
- /* set display mode */
- lcd_write_cmd(LCD_CMD_DISPLAY_CTRL
- | ((lcd.flags & LCD_FLAG_D)
- ? LCD_CMD_DISPLAY_ON : 0)
- | ((lcd.flags & LCD_FLAG_C)
- ? LCD_CMD_CURSOR_ON : 0)
- | ((lcd.flags & LCD_FLAG_B)
- ? LCD_CMD_BLINK_ON : 0));
- /* check whether one of F,N flags was changed */
- else if ((oldflags ^ lcd.flags) & (LCD_FLAG_F | LCD_FLAG_N))
- lcd_write_cmd(LCD_CMD_FUNCTION_SET
- | LCD_CMD_DATA_LEN_8BITS
- | ((lcd.flags & LCD_FLAG_F)
- ? LCD_CMD_FONT_5X10_DOTS
- : 0)
- | ((lcd.flags & LCD_FLAG_N)
- ? LCD_CMD_TWO_LINES
- : 0));
- /* check whether L flag was changed */
- else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L))
- lcd_backlight(!!(lcd.flags & LCD_FLAG_L));
- }
-
- return processed;
-}
-
-static void lcd_write_char(char c)
-{
- /* first, we'll test if we're in escape mode */
- if ((c != '\n') && lcd.esc_seq.len >= 0) {
- /* yes, let's add this char to the buffer */
- lcd.esc_seq.buf[lcd.esc_seq.len++] = c;
- lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
- } else {
- /* aborts any previous escape sequence */
- lcd.esc_seq.len = -1;
-
- switch (c) {
- case LCD_ESCAPE_CHAR:
- /* start of an escape sequence */
- lcd.esc_seq.len = 0;
- lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
- break;
- case '\b':
- /* go back one char and clear it */
- if (lcd.addr.x > 0) {
- /*
- * check if we're not at the
- * end of the line
- */
- if (lcd.addr.x < lcd.bwidth)
- /* back one char */
- lcd_write_cmd(LCD_CMD_SHIFT);
- lcd.addr.x--;
- }
- /* replace with a space */
- lcd_write_data(' ');
- /* back one char again */
- lcd_write_cmd(LCD_CMD_SHIFT);
- break;
- case '\014':
- /* quickly clear the display */
- lcd_clear_fast();
- break;
- case '\n':
- /*
- * flush the remainder of the current line and
- * go to the beginning of the next line
- */
- for (; lcd.addr.x < lcd.bwidth; lcd.addr.x++)
- lcd_write_data(' ');
- lcd.addr.x = 0;
- lcd.addr.y = (lcd.addr.y + 1) % lcd.height;
- lcd_gotoxy();
- break;
- case '\r':
- /* go to the beginning of the same line */
- lcd.addr.x = 0;
- lcd_gotoxy();
- break;
- case '\t':
- /* print a space instead of the tab */
- lcd_print(' ');
- break;
- default:
- /* simply print this char */
- lcd_print(c);
- break;
- }
- }
+ charlcd = charlcd_alloc(0);
+ if (!charlcd)
+ return;
/*
- * now we'll see if we're in an escape mode and if the current
- * escape sequence can be understood.
+ * Init lcd struct with load-time values to preserve exact
+ * current functionality (at least for now).
*/
- if (lcd.esc_seq.len >= 2) {
- int processed = 0;
-
- if (!strcmp(lcd.esc_seq.buf, "[2J")) {
- /* clear the display */
- lcd_clear_fast();
- processed = 1;
- } else if (!strcmp(lcd.esc_seq.buf, "[H")) {
- /* cursor to home */
- lcd_home();
- processed = 1;
- }
- /* codes starting with ^[[L */
- else if ((lcd.esc_seq.len >= 3) &&
- (lcd.esc_seq.buf[0] == '[') &&
- (lcd.esc_seq.buf[1] == 'L')) {
- processed = handle_lcd_special_code();
- }
-
- /* LCD special escape codes */
- /*
- * flush the escape sequence if it's been processed
- * or if it is getting too long.
- */
- if (processed || (lcd.esc_seq.len >= LCD_ESCAPE_LEN))
- lcd.esc_seq.len = -1;
- } /* escape codes */
-}
+ charlcd->height = lcd_height;
+ charlcd->width = lcd_width;
+ charlcd->bwidth = lcd_bwidth;
+ charlcd->hwidth = lcd_hwidth;
-static ssize_t lcd_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
-{
- const char __user *tmp = buf;
- char c;
-
- for (; count-- > 0; (*ppos)++, tmp++) {
- if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
- /*
- * let's be a little nice with other processes
- * that need some CPU
- */
- schedule();
-
- if (get_user(c, tmp))
- return -EFAULT;
-
- lcd_write_char(c);
- }
-
- return tmp - buf;
-}
-
-static int lcd_open(struct inode *inode, struct file *file)
-{
- if (!atomic_dec_and_test(&lcd_available))
- return -EBUSY; /* open only once at a time */
-
- if (file->f_mode & FMODE_READ) /* device is write-only */
- return -EPERM;
-
- if (lcd.must_clear) {
- lcd_clear_display();
- lcd.must_clear = false;
- }
- return nonseekable_open(inode, file);
-}
-
-static int lcd_release(struct inode *inode, struct file *file)
-{
- atomic_inc(&lcd_available);
- return 0;
-}
-
-static const struct file_operations lcd_fops = {
- .write = lcd_write,
- .open = lcd_open,
- .release = lcd_release,
- .llseek = no_llseek,
-};
-
-static struct miscdevice lcd_dev = {
- .minor = LCD_MINOR,
- .name = "lcd",
- .fops = &lcd_fops,
-};
-
-/* public function usable from the kernel for any purpose */
-static void panel_lcd_print(const char *s)
-{
- const char *tmp = s;
- int count = strlen(s);
-
- if (lcd.enabled && lcd.initialized) {
- for (; count-- > 0; tmp++) {
- if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
- /*
- * let's be a little nice with other processes
- * that need some CPU
- */
- schedule();
-
- lcd_write_char(*tmp);
- }
- }
-}
-
-/* initialize the LCD driver */
-static void lcd_init(void)
-{
switch (selected_lcd_type) {
case LCD_TYPE_OLD:
/* parallel mode, 8 bits */
@@ -1500,10 +924,10 @@ static void lcd_init(void)
lcd.pins.e = PIN_STROBE;
lcd.pins.rs = PIN_AUTOLF;
- lcd.width = 40;
- lcd.bwidth = 40;
- lcd.hwidth = 64;
- lcd.height = 2;
+ charlcd->width = 40;
+ charlcd->bwidth = 40;
+ charlcd->hwidth = 64;
+ charlcd->height = 2;
break;
case LCD_TYPE_KS0074:
/* serial mode, ks0074 */
@@ -1513,10 +937,10 @@ static void lcd_init(void)
lcd.pins.cl = PIN_STROBE;
lcd.pins.da = PIN_D0;
- lcd.width = 16;
- lcd.bwidth = 40;
- lcd.hwidth = 16;
- lcd.height = 2;
+ charlcd->width = 16;
+ charlcd->bwidth = 40;
+ charlcd->hwidth = 16;
+ charlcd->height = 2;
break;
case LCD_TYPE_NEXCOM:
/* parallel mode, 8 bits, generic */
@@ -1526,10 +950,10 @@ static void lcd_init(void)
lcd.pins.rs = PIN_SELECP;
lcd.pins.rw = PIN_INITP;
- lcd.width = 16;
- lcd.bwidth = 40;
- lcd.hwidth = 64;
- lcd.height = 2;
+ charlcd->width = 16;
+ charlcd->bwidth = 40;
+ charlcd->hwidth = 64;
+ charlcd->height = 2;
break;
case LCD_TYPE_CUSTOM:
/* customer-defined */
@@ -1545,22 +969,22 @@ static void lcd_init(void)
lcd.pins.e = PIN_STROBE;
lcd.pins.rs = PIN_SELECP;
- lcd.width = 16;
- lcd.bwidth = 40;
- lcd.hwidth = 64;
- lcd.height = 2;
+ charlcd->width = 16;
+ charlcd->bwidth = 40;
+ charlcd->hwidth = 64;
+ charlcd->height = 2;
break;
}
/* Overwrite with module params set on loading */
if (lcd_height != NOT_SET)
- lcd.height = lcd_height;
+ charlcd->height = lcd_height;
if (lcd_width != NOT_SET)
- lcd.width = lcd_width;
+ charlcd->width = lcd_width;
if (lcd_bwidth != NOT_SET)
- lcd.bwidth = lcd_bwidth;
+ charlcd->bwidth = lcd_bwidth;
if (lcd_hwidth != NOT_SET)
- lcd.hwidth = lcd_hwidth;
+ charlcd->hwidth = lcd_hwidth;
if (lcd_charset != NOT_SET)
lcd.charset = lcd_charset;
if (lcd_proto != NOT_SET)
@@ -1579,19 +1003,17 @@ static void lcd_init(void)
lcd.pins.bl = lcd_bl_pin;
/* this is used to catch wrong and default values */
- if (lcd.width <= 0)
- lcd.width = DEFAULT_LCD_WIDTH;
- if (lcd.bwidth <= 0)
- lcd.bwidth = DEFAULT_LCD_BWIDTH;
- if (lcd.hwidth <= 0)
- lcd.hwidth = DEFAULT_LCD_HWIDTH;
- if (lcd.height <= 0)
- lcd.height = DEFAULT_LCD_HEIGHT;
+ if (charlcd->width <= 0)
+ charlcd->width = DEFAULT_LCD_WIDTH;
+ if (charlcd->bwidth <= 0)
+ charlcd->bwidth = DEFAULT_LCD_BWIDTH;
+ if (charlcd->hwidth <= 0)
+ charlcd->hwidth = DEFAULT_LCD_HWIDTH;
+ if (charlcd->height <= 0)
+ charlcd->height = DEFAULT_LCD_HEIGHT;
if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
- lcd_write_cmd = lcd_write_cmd_s;
- lcd_write_data = lcd_write_data_s;
- lcd_clear_fast = lcd_clear_fast_s;
+ charlcd->ops = &charlcd_serial_ops;
if (lcd.pins.cl == PIN_NOT_SET)
lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
@@ -1599,9 +1021,7 @@ static void lcd_init(void)
lcd.pins.da = DEFAULT_LCD_PIN_SDA;
} else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */
- lcd_write_cmd = lcd_write_cmd_p8;
- lcd_write_data = lcd_write_data_p8;
- lcd_clear_fast = lcd_clear_fast_p8;
+ charlcd->ops = &charlcd_parallel_ops;
if (lcd.pins.e == PIN_NOT_SET)
lcd.pins.e = DEFAULT_LCD_PIN_E;
@@ -1610,9 +1030,7 @@ static void lcd_init(void)
if (lcd.pins.rw == PIN_NOT_SET)
lcd.pins.rw = DEFAULT_LCD_PIN_RW;
} else {
- lcd_write_cmd = lcd_write_cmd_tilcd;
- lcd_write_data = lcd_write_data_tilcd;
- lcd_clear_fast = lcd_clear_fast_tilcd;
+ charlcd->ops = &charlcd_tilcd_ops;
}
if (lcd.pins.bl == PIN_NOT_SET)
@@ -1635,14 +1053,9 @@ static void lcd_init(void)
lcd.charset = DEFAULT_LCD_CHARSET;
if (lcd.charset == LCD_CHARSET_KS0074)
- lcd_char_conv = lcd_char_conv_ks0074;
+ charlcd->char_conv = lcd_char_conv_ks0074;
else
- lcd_char_conv = NULL;
-
- if (lcd.pins.bl != PIN_NONE) {
- mutex_init(&lcd.bl_tempo_lock);
- INIT_DELAYED_WORK(&lcd.bl_work, lcd_bl_off);
- }
+ charlcd->char_conv = NULL;
pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
lcd_bits[LCD_PORT_C][LCD_BIT_E]);
@@ -1657,25 +1070,8 @@ static void lcd_init(void)
pin_to_bits(lcd.pins.da, lcd_bits[LCD_PORT_D][LCD_BIT_DA],
lcd_bits[LCD_PORT_C][LCD_BIT_DA]);
- /*
- * before this line, we must NOT send anything to the display.
- * Since lcd_init_display() needs to write data, we have to
- * enable mark the LCD initialized just before.
- */
+ lcd.charlcd = charlcd;
lcd.initialized = true;
- lcd_init_display();
-
- /* display a short message */
-#ifdef CONFIG_PANEL_CHANGE_MESSAGE
-#ifdef CONFIG_PANEL_BOOT_MESSAGE
- panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
-#endif
-#else
- panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE);
-#endif
- /* clear the display on the next device opening */
- lcd.must_clear = true;
- lcd_home();
}
/*
@@ -2011,7 +1407,7 @@ static void panel_scan_timer(void)
}
if (keypressed && lcd.enabled && lcd.initialized)
- lcd_poke();
+ charlcd_poke(lcd.charlcd);
mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
}
@@ -2175,35 +1571,6 @@ static void keypad_init(void)
/* device initialization */
/**************************************************/
-static int panel_notify_sys(struct notifier_block *this, unsigned long code,
- void *unused)
-{
- if (lcd.enabled && lcd.initialized) {
- switch (code) {
- case SYS_DOWN:
- panel_lcd_print
- ("\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
- break;
- case SYS_HALT:
- panel_lcd_print
- ("\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
- break;
- case SYS_POWER_OFF:
- panel_lcd_print("\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
- break;
- default:
- break;
- }
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block panel_notifier = {
- panel_notify_sys,
- NULL,
- 0
-};
-
static void panel_attach(struct parport *port)
{
struct pardev_cb panel_cb;
@@ -2239,7 +1606,7 @@ static void panel_attach(struct parport *port)
*/
if (lcd.enabled) {
lcd_init();
- if (misc_register(&lcd_dev))
+ if (!lcd.charlcd || charlcd_register(lcd.charlcd))
goto err_unreg_device;
}
@@ -2248,13 +1615,14 @@ static void panel_attach(struct parport *port)
if (misc_register(&keypad_dev))
goto err_lcd_unreg;
}
- register_reboot_notifier(&panel_notifier);
return;
err_lcd_unreg:
if (lcd.enabled)
- misc_deregister(&lcd_dev);
+ charlcd_unregister(lcd.charlcd);
err_unreg_device:
+ kfree(lcd.charlcd);
+ lcd.charlcd = NULL;
parport_unregister_device(pprt);
pprt = NULL;
}
@@ -2278,20 +1646,16 @@ static void panel_detach(struct parport *port)
}
if (lcd.enabled) {
- panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
- misc_deregister(&lcd_dev);
- if (lcd.pins.bl != PIN_NONE) {
- cancel_delayed_work_sync(&lcd.bl_work);
- __lcd_backlight(0);
- }
+ charlcd_unregister(lcd.charlcd);
lcd.initialized = false;
+ kfree(lcd.charlcd);
+ lcd.charlcd = NULL;
}
/* TODO: free all input signals */
parport_release(pprt);
parport_unregister_device(pprt);
pprt = NULL;
- unregister_reboot_notifier(&panel_notifier);
}
static struct parport_driver panel_driver = {
@@ -2369,10 +1733,6 @@ static int __init panel_init_module(void)
* Init lcd struct with load-time values to preserve exact
* current functionality (at least for now).
*/
- lcd.height = lcd_height;
- lcd.width = lcd_width;
- lcd.bwidth = lcd_bwidth;
- lcd.hwidth = lcd_hwidth;
lcd.charset = lcd_charset;
lcd.proto = lcd_proto;
lcd.pins.e = lcd_e_pin;
@@ -2381,9 +1741,6 @@ static int __init panel_init_module(void)
lcd.pins.cl = lcd_cl_pin;
lcd.pins.da = lcd_da_pin;
lcd.pins.bl = lcd_bl_pin;
-
- /* Leave it for now, just in case */
- lcd.esc_seq.len = -1;
}
switch (selected_keypad_type) {
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 77513195f50e..8bae3731d039 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -421,41 +421,6 @@ static void dev_release(struct device *dev)
}
/**
- * ubi_sysfs_init - initialize sysfs for an UBI device.
- * @ubi: UBI device description object
- * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was
- * taken
- *
- * This function returns zero in case of success and a negative error code in
- * case of failure.
- */
-static int ubi_sysfs_init(struct ubi_device *ubi, int *ref)
-{
- int err;
-
- ubi->dev.release = dev_release;
- ubi->dev.devt = ubi->cdev.dev;
- ubi->dev.class = &ubi_class;
- ubi->dev.groups = ubi_dev_groups;
- dev_set_name(&ubi->dev, UBI_NAME_STR"%d", ubi->ubi_num);
- err = device_register(&ubi->dev);
- if (err)
- return err;
-
- *ref = 1;
- return 0;
-}
-
-/**
- * ubi_sysfs_close - close sysfs for an UBI device.
- * @ubi: UBI device description object
- */
-static void ubi_sysfs_close(struct ubi_device *ubi)
-{
- device_unregister(&ubi->dev);
-}
-
-/**
* kill_volumes - destroy all user volumes.
* @ubi: UBI device description object
*/
@@ -471,27 +436,19 @@ static void kill_volumes(struct ubi_device *ubi)
/**
* uif_init - initialize user interfaces for an UBI device.
* @ubi: UBI device description object
- * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was
- * taken, otherwise set to %0
*
* This function initializes various user interfaces for an UBI device. If the
* initialization fails at an early stage, this function frees all the
- * resources it allocated, returns an error, and @ref is set to %0. However,
- * if the initialization fails after the UBI device was registered in the
- * driver core subsystem, this function takes a reference to @ubi->dev, because
- * otherwise the release function ('dev_release()') would free whole @ubi
- * object. The @ref argument is set to %1 in this case. The caller has to put
- * this reference.
+ * resources it allocated, returns an error.
*
* This function returns zero in case of success and a negative error code in
* case of failure.
*/
-static int uif_init(struct ubi_device *ubi, int *ref)
+static int uif_init(struct ubi_device *ubi)
{
int i, err;
dev_t dev;
- *ref = 0;
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
/*
@@ -508,20 +465,17 @@ static int uif_init(struct ubi_device *ubi, int *ref)
return err;
}
+ ubi->dev.devt = dev;
+
ubi_assert(MINOR(dev) == 0);
cdev_init(&ubi->cdev, &ubi_cdev_operations);
dbg_gen("%s major is %u", ubi->ubi_name, MAJOR(dev));
ubi->cdev.owner = THIS_MODULE;
- err = cdev_add(&ubi->cdev, dev, 1);
- if (err) {
- ubi_err(ubi, "cannot add character device");
- goto out_unreg;
- }
-
- err = ubi_sysfs_init(ubi, ref);
+ dev_set_name(&ubi->dev, UBI_NAME_STR "%d", ubi->ubi_num);
+ err = cdev_device_add(&ubi->cdev, &ubi->dev);
if (err)
- goto out_sysfs;
+ goto out_unreg;
for (i = 0; i < ubi->vtbl_slots; i++)
if (ubi->volumes[i]) {
@@ -536,11 +490,7 @@ static int uif_init(struct ubi_device *ubi, int *ref)
out_volumes:
kill_volumes(ubi);
-out_sysfs:
- if (*ref)
- get_device(&ubi->dev);
- ubi_sysfs_close(ubi);
- cdev_del(&ubi->cdev);
+ cdev_device_del(&ubi->cdev, &ubi->dev);
out_unreg:
unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
ubi_err(ubi, "cannot initialize UBI %s, error %d",
@@ -559,8 +509,7 @@ out_unreg:
static void uif_close(struct ubi_device *ubi)
{
kill_volumes(ubi);
- ubi_sysfs_close(ubi);
- cdev_del(&ubi->cdev);
+ cdev_device_del(&ubi->cdev, &ubi->dev);
unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
}
@@ -857,7 +806,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
int vid_hdr_offset, int max_beb_per1024)
{
struct ubi_device *ubi;
- int i, err, ref = 0;
+ int i, err;
if (max_beb_per1024 < 0 || max_beb_per1024 > MAX_MTD_UBI_BEB_LIMIT)
return -EINVAL;
@@ -919,6 +868,11 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
if (!ubi)
return -ENOMEM;
+ device_initialize(&ubi->dev);
+ ubi->dev.release = dev_release;
+ ubi->dev.class = &ubi_class;
+ ubi->dev.groups = ubi_dev_groups;
+
ubi->mtd = mtd;
ubi->ubi_num = ubi_num;
ubi->vid_hdr_offset = vid_hdr_offset;
@@ -995,7 +949,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
/* Make device "available" before it becomes accessible via sysfs */
ubi_devices[ubi_num] = ubi;
- err = uif_init(ubi, &ref);
+ err = uif_init(ubi);
if (err)
goto out_detach;
@@ -1045,8 +999,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
out_debugfs:
ubi_debugfs_exit_dev(ubi);
out_uif:
- get_device(&ubi->dev);
- ubi_assert(ref);
uif_close(ubi);
out_detach:
ubi_devices[ubi_num] = NULL;
@@ -1056,10 +1008,7 @@ out_detach:
out_free:
vfree(ubi->peb_buf);
vfree(ubi->fm_buf);
- if (ref)
- put_device(&ubi->dev);
- else
- kfree(ubi);
+ put_device(&ubi->dev);
return err;
}
@@ -1120,12 +1069,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
if (ubi->bgt_thread)
kthread_stop(ubi->bgt_thread);
- /*
- * Get a reference to the device in order to prevent 'dev_release()'
- * from freeing the @ubi object.
- */
- get_device(&ubi->dev);
-
ubi_debugfs_exit_dev(ubi);
uif_close(ubi);
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 7ac78c13dd1c..85237cf661f9 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -155,11 +155,10 @@ static void vol_release(struct device *dev)
*/
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
{
- int i, err, vol_id = req->vol_id, do_free = 1;
+ int i, err, vol_id = req->vol_id;
struct ubi_volume *vol;
struct ubi_vtbl_record vtbl_rec;
struct ubi_eba_table *eba_tbl = NULL;
- dev_t dev;
if (ubi->ro_mode)
return -EROFS;
@@ -168,6 +167,12 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
if (!vol)
return -ENOMEM;
+ device_initialize(&vol->dev);
+ vol->dev.release = vol_release;
+ vol->dev.parent = &ubi->dev;
+ vol->dev.class = &ubi_class;
+ vol->dev.groups = volume_dev_groups;
+
spin_lock(&ubi->volumes_lock);
if (vol_id == UBI_VOL_NUM_AUTO) {
/* Find unused volume ID */
@@ -268,24 +273,13 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
/* Register character device for the volume */
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
vol->cdev.owner = THIS_MODULE;
- dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1);
- err = cdev_add(&vol->cdev, dev, 1);
- if (err) {
- ubi_err(ubi, "cannot add character device");
- goto out_mapping;
- }
-
- vol->dev.release = vol_release;
- vol->dev.parent = &ubi->dev;
- vol->dev.devt = dev;
- vol->dev.class = &ubi_class;
- vol->dev.groups = volume_dev_groups;
+ vol->dev.devt = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1);
dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
- err = device_register(&vol->dev);
+ err = cdev_device_add(&vol->cdev, &vol->dev);
if (err) {
- ubi_err(ubi, "cannot register device");
- goto out_cdev;
+ ubi_err(ubi, "cannot add device");
+ goto out_mapping;
}
/* Fill volume table record */
@@ -318,28 +312,17 @@ out_sysfs:
* We have registered our device, we should not free the volume
* description object in this function in case of an error - it is
* freed by the release function.
- *
- * Get device reference to prevent the release function from being
- * called just after sysfs has been closed.
*/
- do_free = 0;
- get_device(&vol->dev);
- device_unregister(&vol->dev);
-out_cdev:
- cdev_del(&vol->cdev);
+ cdev_device_del(&vol->cdev, &vol->dev);
out_mapping:
- if (do_free)
- ubi_eba_destroy_table(eba_tbl);
+ ubi_eba_destroy_table(eba_tbl);
out_acc:
spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs -= vol->reserved_pebs;
ubi->avail_pebs += vol->reserved_pebs;
out_unlock:
spin_unlock(&ubi->volumes_lock);
- if (do_free)
- kfree(vol);
- else
- put_device(&vol->dev);
+ put_device(&vol->dev);
ubi_err(ubi, "cannot create volume %d, error %d", vol_id, err);
return err;
}
@@ -391,8 +374,8 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
goto out_err;
}
- cdev_del(&vol->cdev);
- device_unregister(&vol->dev);
+ cdev_device_del(&vol->cdev, &vol->dev);
+ put_device(&vol->dev);
spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs -= reserved_pebs;
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index 6f09da4dadb8..6aa120cd0574 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -391,7 +391,6 @@ static int ec_device_probe(struct platform_device *pdev)
int retval = -ENOMEM;
struct device *dev = &pdev->dev;
struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
- dev_t devno = MKDEV(ec_major, pdev->id);
struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
if (!ec)
@@ -407,23 +406,11 @@ static int ec_device_probe(struct platform_device *pdev)
cdev_init(&ec->cdev, &fops);
/*
- * Add the character device
- * Link cdev to the class device to be sure device is not used
- * before unbinding it.
- */
- ec->cdev.kobj.parent = &ec->class_dev.kobj;
- retval = cdev_add(&ec->cdev, devno, 1);
- if (retval) {
- dev_err(dev, ": failed to add character device\n");
- goto cdev_add_failed;
- }
-
- /*
* Add the class device
* Link to the character device for creating the /dev entry
* in devtmpfs.
*/
- ec->class_dev.devt = ec->cdev.dev;
+ ec->class_dev.devt = MKDEV(ec_major, pdev->id);
ec->class_dev.class = &cros_class;
ec->class_dev.parent = dev;
ec->class_dev.release = __remove;
@@ -431,13 +418,13 @@ static int ec_device_probe(struct platform_device *pdev)
retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
if (retval) {
dev_err(dev, "dev_set_name failed => %d\n", retval);
- goto set_named_failed;
+ goto failed;
}
- retval = device_add(&ec->class_dev);
+ retval = cdev_device_add(&ec->cdev, &ec->class_dev);
if (retval) {
- dev_err(dev, "device_register failed => %d\n", retval);
- goto dev_reg_failed;
+ dev_err(dev, "cdev_device_add failed => %d\n", retval);
+ goto failed;
}
/* check whether this EC is a sensor hub. */
@@ -446,12 +433,8 @@ static int ec_device_probe(struct platform_device *pdev)
return 0;
-dev_reg_failed:
-set_named_failed:
- dev_set_drvdata(dev, NULL);
- cdev_del(&ec->cdev);
-cdev_add_failed:
- kfree(ec);
+failed:
+ put_device(&ec->class_dev);
return retval;
}
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index 2f07cd615665..6eb0db37dd88 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -64,6 +64,43 @@ static int pps_cdev_fasync(int fd, struct file *file, int on)
return fasync_helper(fd, file, on, &pps->async_queue);
}
+static int pps_cdev_pps_fetch(struct pps_device *pps, struct pps_fdata *fdata)
+{
+ unsigned int ev = pps->last_ev;
+ int err = 0;
+
+ /* Manage the timeout */
+ if (fdata->timeout.flags & PPS_TIME_INVALID)
+ err = wait_event_interruptible(pps->queue,
+ ev != pps->last_ev);
+ else {
+ unsigned long ticks;
+
+ dev_dbg(pps->dev, "timeout %lld.%09d\n",
+ (long long) fdata->timeout.sec,
+ fdata->timeout.nsec);
+ ticks = fdata->timeout.sec * HZ;
+ ticks += fdata->timeout.nsec / (NSEC_PER_SEC / HZ);
+
+ if (ticks != 0) {
+ err = wait_event_interruptible_timeout(
+ pps->queue,
+ ev != pps->last_ev,
+ ticks);
+ if (err == 0)
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* Check for pending signals */
+ if (err == -ERESTARTSYS) {
+ dev_dbg(pps->dev, "pending signal caught\n");
+ return -EINTR;
+ }
+
+ return 0;
+}
+
static long pps_cdev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -144,7 +181,6 @@ static long pps_cdev_ioctl(struct file *file,
case PPS_FETCH: {
struct pps_fdata fdata;
- unsigned int ev;
dev_dbg(pps->dev, "PPS_FETCH\n");
@@ -152,36 +188,9 @@ static long pps_cdev_ioctl(struct file *file,
if (err)
return -EFAULT;
- ev = pps->last_ev;
-
- /* Manage the timeout */
- if (fdata.timeout.flags & PPS_TIME_INVALID)
- err = wait_event_interruptible(pps->queue,
- ev != pps->last_ev);
- else {
- unsigned long ticks;
-
- dev_dbg(pps->dev, "timeout %lld.%09d\n",
- (long long) fdata.timeout.sec,
- fdata.timeout.nsec);
- ticks = fdata.timeout.sec * HZ;
- ticks += fdata.timeout.nsec / (NSEC_PER_SEC / HZ);
-
- if (ticks != 0) {
- err = wait_event_interruptible_timeout(
- pps->queue,
- ev != pps->last_ev,
- ticks);
- if (err == 0)
- return -ETIMEDOUT;
- }
- }
-
- /* Check for pending signals */
- if (err == -ERESTARTSYS) {
- dev_dbg(pps->dev, "pending signal caught\n");
- return -EINTR;
- }
+ err = pps_cdev_pps_fetch(pps, &fdata);
+ if (err)
+ return err;
/* Return the fetched timestamp */
spin_lock_irq(&pps->lock);
@@ -242,6 +251,57 @@ static long pps_cdev_ioctl(struct file *file,
return 0;
}
+#ifdef CONFIG_COMPAT
+static long pps_cdev_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct pps_device *pps = file->private_data;
+ void __user *uarg = (void __user *) arg;
+
+ cmd = _IOC(_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), sizeof(void *));
+
+ if (cmd == PPS_FETCH) {
+ struct pps_fdata_compat compat;
+ struct pps_fdata fdata;
+ int err;
+
+ dev_dbg(pps->dev, "PPS_FETCH\n");
+
+ err = copy_from_user(&compat, uarg, sizeof(struct pps_fdata_compat));
+ if (err)
+ return -EFAULT;
+
+ memcpy(&fdata.timeout, &compat.timeout,
+ sizeof(struct pps_ktime_compat));
+
+ err = pps_cdev_pps_fetch(pps, &fdata);
+ if (err)
+ return err;
+
+ /* Return the fetched timestamp */
+ spin_lock_irq(&pps->lock);
+
+ compat.info.assert_sequence = pps->assert_sequence;
+ compat.info.clear_sequence = pps->clear_sequence;
+ compat.info.current_mode = pps->current_mode;
+
+ memcpy(&compat.info.assert_tu, &pps->assert_tu,
+ sizeof(struct pps_ktime_compat));
+ memcpy(&compat.info.clear_tu, &pps->clear_tu,
+ sizeof(struct pps_ktime_compat));
+
+ spin_unlock_irq(&pps->lock);
+
+ return copy_to_user(uarg, &compat,
+ sizeof(struct pps_fdata_compat)) ? -EFAULT : 0;
+ }
+
+ return pps_cdev_ioctl(file, cmd, arg);
+}
+#else
+#define pps_cdev_compat_ioctl NULL
+#endif
+
static int pps_cdev_open(struct inode *inode, struct file *file)
{
struct pps_device *pps = container_of(inode->i_cdev,
@@ -268,6 +328,7 @@ static const struct file_operations pps_cdev_fops = {
.llseek = no_llseek,
.poll = pps_cdev_poll,
.fasync = pps_cdev_fasync,
+ .compat_ioctl = pps_cdev_compat_ioctl,
.unlocked_ioctl = pps_cdev_ioctl,
.open = pps_cdev_open,
.release = pps_cdev_release,
diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c
index 50b617af81bd..5beb0c361076 100644
--- a/drivers/rapidio/devices/rio_mport_cdev.c
+++ b/drivers/rapidio/devices/rio_mport_cdev.c
@@ -2444,31 +2444,25 @@ static struct mport_dev *mport_cdev_add(struct rio_mport *mport)
mutex_init(&md->buf_mutex);
mutex_init(&md->file_mutex);
INIT_LIST_HEAD(&md->file_list);
- cdev_init(&md->cdev, &mport_fops);
- md->cdev.owner = THIS_MODULE;
- ret = cdev_add(&md->cdev, MKDEV(MAJOR(dev_number), mport->id), 1);
- if (ret < 0) {
- kfree(md);
- rmcd_error("Unable to register a device, err=%d", ret);
- return NULL;
- }
- md->dev.devt = md->cdev.dev;
+ device_initialize(&md->dev);
+ md->dev.devt = MKDEV(MAJOR(dev_number), mport->id);
md->dev.class = dev_class;
md->dev.parent = &mport->dev;
md->dev.release = mport_device_release;
dev_set_name(&md->dev, DEV_NAME "%d", mport->id);
atomic_set(&md->active, 1);
- ret = device_register(&md->dev);
+ cdev_init(&md->cdev, &mport_fops);
+ md->cdev.owner = THIS_MODULE;
+
+ ret = cdev_device_add(&md->cdev, &md->dev);
if (ret) {
rmcd_error("Failed to register mport %d (err=%d)",
mport->id, ret);
goto err_cdev;
}
- get_device(&md->dev);
-
INIT_LIST_HEAD(&md->doorbells);
spin_lock_init(&md->db_lock);
INIT_LIST_HEAD(&md->portwrites);
@@ -2513,8 +2507,7 @@ static struct mport_dev *mport_cdev_add(struct rio_mport *mport)
return md;
err_cdev:
- cdev_del(&md->cdev);
- kfree(md);
+ put_device(&md->dev);
return NULL;
}
@@ -2578,7 +2571,7 @@ static void mport_cdev_remove(struct mport_dev *md)
atomic_set(&md->active, 0);
mport_cdev_terminate_dma(md);
rio_del_mport_pw_handler(md->mport, md, rio_mport_pw_handler);
- cdev_del(&(md->cdev));
+ cdev_device_del(&md->cdev, &md->dev);
mport_cdev_kill_fasync(md);
flush_workqueue(dma_wq);
@@ -2603,7 +2596,6 @@ static void mport_cdev_remove(struct mport_dev *md)
rio_release_inb_dbell(md->mport, 0, 0x0fff);
- device_unregister(&md->dev);
put_device(&md->dev);
}
diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
index eda41563d06d..73e4b407f162 100644
--- a/drivers/rapidio/rio-sysfs.c
+++ b/drivers/rapidio/rio-sysfs.c
@@ -108,15 +108,11 @@ static struct attribute *rio_dev_attrs[] = {
&dev_attr_lprev.attr,
&dev_attr_destid.attr,
&dev_attr_modalias.attr,
- NULL,
-};
-static const struct attribute_group rio_dev_group = {
- .attrs = rio_dev_attrs,
-};
-
-const struct attribute_group *rio_dev_groups[] = {
- &rio_dev_group,
+ /* Switch-only attributes */
+ &dev_attr_routes.attr,
+ &dev_attr_lnext.attr,
+ &dev_attr_hopcount.attr,
NULL,
};
@@ -259,46 +255,40 @@ static struct bin_attribute rio_config_attr = {
.write = rio_write_config,
};
-/**
- * rio_create_sysfs_dev_files - create RIO specific sysfs files
- * @rdev: device whose entries should be created
- *
- * Create files when @rdev is added to sysfs.
- */
-int rio_create_sysfs_dev_files(struct rio_dev *rdev)
-{
- int err = 0;
-
- err = device_create_bin_file(&rdev->dev, &rio_config_attr);
+static struct bin_attribute *rio_dev_bin_attrs[] = {
+ &rio_config_attr,
+ NULL,
+};
- if (!err && (rdev->pef & RIO_PEF_SWITCH)) {
- err |= device_create_file(&rdev->dev, &dev_attr_routes);
- err |= device_create_file(&rdev->dev, &dev_attr_lnext);
- err |= device_create_file(&rdev->dev, &dev_attr_hopcount);
+static umode_t rio_dev_is_attr_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct rio_dev *rdev = to_rio_dev(kobj_to_dev(kobj));
+ umode_t mode = attr->mode;
+
+ if (!(rdev->pef & RIO_PEF_SWITCH) &&
+ (attr == &dev_attr_routes.attr ||
+ attr == &dev_attr_lnext.attr ||
+ attr == &dev_attr_hopcount.attr)) {
+ /*
+ * Hide switch-specific attributes for a non-switch device.
+ */
+ mode = 0;
}
- if (err)
- pr_warning("RIO: Failed to create attribute file(s) for %s\n",
- rio_name(rdev));
-
- return err;
+ return mode;
}
-/**
- * rio_remove_sysfs_dev_files - cleanup RIO specific sysfs files
- * @rdev: device whose entries we should free
- *
- * Cleanup when @rdev is removed from sysfs.
- */
-void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
-{
- device_remove_bin_file(&rdev->dev, &rio_config_attr);
- if (rdev->pef & RIO_PEF_SWITCH) {
- device_remove_file(&rdev->dev, &dev_attr_routes);
- device_remove_file(&rdev->dev, &dev_attr_lnext);
- device_remove_file(&rdev->dev, &dev_attr_hopcount);
- }
-}
+static const struct attribute_group rio_dev_group = {
+ .attrs = rio_dev_attrs,
+ .is_visible = rio_dev_is_attr_visible,
+ .bin_attrs = rio_dev_bin_attrs,
+};
+
+const struct attribute_group *rio_dev_groups[] = {
+ &rio_dev_group,
+ NULL,
+};
static ssize_t bus_scan_store(struct bus_type *bus, const char *buf,
size_t count)
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index 37042858c2db..38d949405618 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -192,8 +192,6 @@ int rio_add_device(struct rio_dev *rdev)
}
spin_unlock(&rio_global_list_lock);
- rio_create_sysfs_dev_files(rdev);
-
return 0;
}
EXPORT_SYMBOL_GPL(rio_add_device);
@@ -220,7 +218,6 @@ void rio_del_device(struct rio_dev *rdev, enum rio_device_state state)
}
}
spin_unlock(&rio_global_list_lock);
- rio_remove_sysfs_dev_files(rdev);
device_unregister(&rdev->dev);
}
EXPORT_SYMBOL_GPL(rio_del_device);
diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h
index 9796b3fee70d..b2abf8576397 100644
--- a/drivers/rapidio/rio.h
+++ b/drivers/rapidio/rio.h
@@ -27,8 +27,6 @@ extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid,
u8 hopcount, u32 from);
extern int rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid,
u8 hopcount);
-extern int rio_create_sysfs_dev_files(struct rio_dev *rdev);
-extern void rio_remove_sysfs_dev_files(struct rio_dev *rdev);
extern int rio_lock_device(struct rio_mport *port, u16 destid,
u8 hopcount, int wait_ms);
extern int rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount);
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 74fd9746aeca..5fb439897fe1 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -195,6 +195,8 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
goto exit_ida;
}
+ device_initialize(&rtc->dev);
+
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
@@ -233,14 +235,19 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
rtc_dev_prepare(rtc);
- err = device_register(&rtc->dev);
+ err = cdev_device_add(&rtc->char_dev, &rtc->dev);
if (err) {
+ dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
+ rtc->name, MAJOR(rtc->dev.devt), rtc->id);
+
/* This will free both memory and the ID */
put_device(&rtc->dev);
goto exit;
+ } else {
+ dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,
+ MAJOR(rtc->dev.devt), rtc->id);
}
- rtc_dev_add_device(rtc);
rtc_proc_add_device(rtc);
dev_info(dev, "rtc core: registered %s as %s\n",
@@ -271,9 +278,8 @@ void rtc_device_unregister(struct rtc_device *rtc)
* Remove innards of this RTC, then disable it, before
* letting any rtc_class_open() users access it again
*/
- rtc_dev_del_device(rtc);
rtc_proc_del_device(rtc);
- device_del(&rtc->dev);
+ cdev_device_del(&rtc->char_dev, &rtc->dev);
rtc->ops = NULL;
mutex_unlock(&rtc->ops_lock);
put_device(&rtc->dev);
diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h
index a098aea197fc..7a4ed2f7c7d7 100644
--- a/drivers/rtc/rtc-core.h
+++ b/drivers/rtc/rtc-core.h
@@ -3,8 +3,6 @@
extern void __init rtc_dev_init(void);
extern void __exit rtc_dev_exit(void);
extern void rtc_dev_prepare(struct rtc_device *rtc);
-extern void rtc_dev_add_device(struct rtc_device *rtc);
-extern void rtc_dev_del_device(struct rtc_device *rtc);
#else
@@ -20,14 +18,6 @@ static inline void rtc_dev_prepare(struct rtc_device *rtc)
{
}
-static inline void rtc_dev_add_device(struct rtc_device *rtc)
-{
-}
-
-static inline void rtc_dev_del_device(struct rtc_device *rtc)
-{
-}
-
#endif
#ifdef CONFIG_RTC_INTF_PROC
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 6dc8f29697ab..e81a8711fea7 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -477,23 +477,6 @@ void rtc_dev_prepare(struct rtc_device *rtc)
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc->char_dev.owner = rtc->owner;
- rtc->char_dev.kobj.parent = &rtc->dev.kobj;
-}
-
-void rtc_dev_add_device(struct rtc_device *rtc)
-{
- if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
- dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
- rtc->name, MAJOR(rtc_devt), rtc->id);
- else
- dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,
- MAJOR(rtc_devt), rtc->id);
-}
-
-void rtc_dev_del_device(struct rtc_device *rtc)
-{
- if (rtc->dev.devt)
- cdev_del(&rtc->char_dev);
}
void __init rtc_dev_init(void)
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c
index e0ce5d2fd14d..4101c3178411 100644
--- a/drivers/scsi/osd/osd_uld.c
+++ b/drivers/scsi/osd/osd_uld.c
@@ -400,9 +400,6 @@ static void __remove(struct device *dev)
kfree(oud->odi.osdname);
- if (oud->cdev.owner)
- cdev_del(&oud->cdev);
-
osd_dev_fini(&oud->od);
scsi_device_put(scsi_device);
@@ -411,7 +408,6 @@ static void __remove(struct device *dev)
if (oud->disk)
put_disk(oud->disk);
- ida_remove(&osd_minor_ida, oud->minor);
kfree(oud);
}
@@ -446,8 +442,20 @@ static int osd_probe(struct device *dev)
if (NULL == oud)
goto err_retract_minor;
+ /* class device member */
+ device_initialize(&oud->class_dev);
dev_set_drvdata(dev, oud);
oud->minor = minor;
+ oud->class_dev.devt = MKDEV(SCSI_OSD_MAJOR, oud->minor);
+ oud->class_dev.class = &osd_uld_class;
+ oud->class_dev.parent = dev;
+ oud->class_dev.release = __remove;
+
+ /* hold one more reference to the scsi_device that will get released
+ * in __release, in case a logout is happening while fs is mounted
+ */
+ scsi_device_get(scsi_device);
+ osd_dev_init(&oud->od, scsi_device);
/* allocate a disk and set it up */
/* FIXME: do we need this since sg has already done that */
@@ -461,59 +469,34 @@ static int osd_probe(struct device *dev)
sprintf(disk->disk_name, "osd%d", oud->minor);
oud->disk = disk;
- /* hold one more reference to the scsi_device that will get released
- * in __release, in case a logout is happening while fs is mounted
- */
- scsi_device_get(scsi_device);
- osd_dev_init(&oud->od, scsi_device);
-
/* Detect the OSD Version */
error = __detect_osd(oud);
if (error) {
OSD_ERR("osd detection failed, non-compatible OSD device\n");
- goto err_put_disk;
+ goto err_free_osd;
}
/* init the char-device for communication with user-mode */
cdev_init(&oud->cdev, &osd_fops);
oud->cdev.owner = THIS_MODULE;
- error = cdev_add(&oud->cdev,
- MKDEV(SCSI_OSD_MAJOR, oud->minor), 1);
- if (error) {
- OSD_ERR("cdev_add failed\n");
- goto err_put_disk;
- }
- /* class device member */
- oud->class_dev.devt = oud->cdev.dev;
- oud->class_dev.class = &osd_uld_class;
- oud->class_dev.parent = dev;
- oud->class_dev.release = __remove;
error = dev_set_name(&oud->class_dev, "%s", disk->disk_name);
if (error) {
OSD_ERR("dev_set_name failed => %d\n", error);
- goto err_put_cdev;
+ goto err_free_osd;
}
- error = device_register(&oud->class_dev);
+ error = cdev_device_add(&oud->cdev, &oud->class_dev);
if (error) {
OSD_ERR("device_register failed => %d\n", error);
- goto err_put_cdev;
+ goto err_free_osd;
}
- get_device(&oud->class_dev);
-
OSD_INFO("osd_probe %s\n", disk->disk_name);
return 0;
-err_put_cdev:
- cdev_del(&oud->cdev);
-err_put_disk:
- scsi_device_put(scsi_device);
- put_disk(disk);
err_free_osd:
- dev_set_drvdata(dev, NULL);
- kfree(oud);
+ put_device(&oud->class_dev);
err_retract_minor:
ida_remove(&osd_minor_ida, minor);
return error;
@@ -530,9 +513,10 @@ static int osd_remove(struct device *dev)
scsi_device);
}
- device_unregister(&oud->class_dev);
-
+ cdev_device_del(&oud->cdev, &oud->class_dev);
+ ida_remove(&osd_minor_ida, oud->minor);
put_device(&oud->class_dev);
+
return 0;
}
diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c
index 0035cf79760a..6a3ead42aba8 100644
--- a/drivers/vme/vme.c
+++ b/drivers/vme/vme.c
@@ -76,9 +76,16 @@ static struct vme_bridge *find_bridge(struct vme_resource *resource)
}
}
-/*
+/**
+ * vme_free_consistent - Allocate contiguous memory.
+ * @resource: Pointer to VME resource.
+ * @size: Size of allocation required.
+ * @dma: Pointer to variable to store physical address of allocation.
+ *
* Allocate a contiguous block of memory for use by the driver. This is used to
* create the buffers for the slave windows.
+ *
+ * Return: Virtual address of allocation on success, NULL on failure.
*/
void *vme_alloc_consistent(struct vme_resource *resource, size_t size,
dma_addr_t *dma)
@@ -111,8 +118,14 @@ void *vme_alloc_consistent(struct vme_resource *resource, size_t size,
}
EXPORT_SYMBOL(vme_alloc_consistent);
-/*
- * Free previously allocated contiguous block of memory.
+/**
+ * vme_free_consistent - Free previously allocated memory.
+ * @resource: Pointer to VME resource.
+ * @size: Size of allocation to free.
+ * @vaddr: Virtual address of allocation.
+ * @dma: Physical address of allocation.
+ *
+ * Free previously allocated block of contiguous memory.
*/
void vme_free_consistent(struct vme_resource *resource, size_t size,
void *vaddr, dma_addr_t dma)
@@ -145,6 +158,16 @@ void vme_free_consistent(struct vme_resource *resource, size_t size,
}
EXPORT_SYMBOL(vme_free_consistent);
+/**
+ * vme_get_size - Helper function returning size of a VME window
+ * @resource: Pointer to VME slave or master resource.
+ *
+ * Determine the size of the VME window provided. This is a helper
+ * function, wrappering the call to vme_master_get or vme_slave_get
+ * depending on the type of window resource handed to it.
+ *
+ * Return: Size of the window on success, zero on failure.
+ */
size_t vme_get_size(struct vme_resource *resource)
{
int enabled, retval;
@@ -259,9 +282,16 @@ static u32 vme_get_aspace(int am)
return 0;
}
-/*
- * Request a slave image with specific attributes, return some unique
- * identifier.
+/**
+ * vme_slave_request - Request a VME slave window resource.
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ * @address: Required VME address space.
+ * @cycle: Required VME data transfer cycle type.
+ *
+ * Request use of a VME window resource capable of being set for the requested
+ * address space and data transfer cycle.
+ *
+ * Return: Pointer to VME resource on success, NULL on failure.
*/
struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address,
u32 cycle)
@@ -327,6 +357,23 @@ err_bus:
}
EXPORT_SYMBOL(vme_slave_request);
+/**
+ * vme_slave_set - Set VME slave window configuration.
+ * @resource: Pointer to VME slave resource.
+ * @enabled: State to which the window should be configured.
+ * @vme_base: Base address for the window.
+ * @size: Size of the VME window.
+ * @buf_base: Based address of buffer used to provide VME slave window storage.
+ * @aspace: VME address space for the VME window.
+ * @cycle: VME data transfer cycle type for the VME window.
+ *
+ * Set configuration for provided VME slave window.
+ *
+ * Return: Zero on success, -EINVAL if operation is not supported on this
+ * device, if an invalid resource has been provided or invalid
+ * attributes are provided. Hardware specific errors may also be
+ * returned.
+ */
int vme_slave_set(struct vme_resource *resource, int enabled,
unsigned long long vme_base, unsigned long long size,
dma_addr_t buf_base, u32 aspace, u32 cycle)
@@ -362,6 +409,21 @@ int vme_slave_set(struct vme_resource *resource, int enabled,
}
EXPORT_SYMBOL(vme_slave_set);
+/**
+ * vme_slave_get - Retrieve VME slave window configuration.
+ * @resource: Pointer to VME slave resource.
+ * @enabled: Pointer to variable for storing state.
+ * @vme_base: Pointer to variable for storing window base address.
+ * @size: Pointer to variable for storing window size.
+ * @buf_base: Pointer to variable for storing slave buffer base address.
+ * @aspace: Pointer to variable for storing VME address space.
+ * @cycle: Pointer to variable for storing VME data transfer cycle type.
+ *
+ * Return configuration for provided VME slave window.
+ *
+ * Return: Zero on success, -EINVAL if operation is not supported on this
+ * device or if an invalid resource has been provided.
+ */
int vme_slave_get(struct vme_resource *resource, int *enabled,
unsigned long long *vme_base, unsigned long long *size,
dma_addr_t *buf_base, u32 *aspace, u32 *cycle)
@@ -386,6 +448,12 @@ int vme_slave_get(struct vme_resource *resource, int *enabled,
}
EXPORT_SYMBOL(vme_slave_get);
+/**
+ * vme_slave_free - Free VME slave window
+ * @resource: Pointer to VME slave resource.
+ *
+ * Free the provided slave resource so that it may be reallocated.
+ */
void vme_slave_free(struct vme_resource *resource)
{
struct vme_slave_resource *slave_image;
@@ -415,9 +483,17 @@ void vme_slave_free(struct vme_resource *resource)
}
EXPORT_SYMBOL(vme_slave_free);
-/*
- * Request a master image with specific attributes, return some unique
- * identifier.
+/**
+ * vme_master_request - Request a VME master window resource.
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ * @address: Required VME address space.
+ * @cycle: Required VME data transfer cycle type.
+ * @dwidth: Required VME data transfer width.
+ *
+ * Request use of a VME window resource capable of being set for the requested
+ * address space, data transfer cycle and width.
+ *
+ * Return: Pointer to VME resource on success, NULL on failure.
*/
struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address,
u32 cycle, u32 dwidth)
@@ -486,6 +562,23 @@ err_bus:
}
EXPORT_SYMBOL(vme_master_request);
+/**
+ * vme_master_set - Set VME master window configuration.
+ * @resource: Pointer to VME master resource.
+ * @enabled: State to which the window should be configured.
+ * @vme_base: Base address for the window.
+ * @size: Size of the VME window.
+ * @aspace: VME address space for the VME window.
+ * @cycle: VME data transfer cycle type for the VME window.
+ * @dwidth: VME data transfer width for the VME window.
+ *
+ * Set configuration for provided VME master window.
+ *
+ * Return: Zero on success, -EINVAL if operation is not supported on this
+ * device, if an invalid resource has been provided or invalid
+ * attributes are provided. Hardware specific errors may also be
+ * returned.
+ */
int vme_master_set(struct vme_resource *resource, int enabled,
unsigned long long vme_base, unsigned long long size, u32 aspace,
u32 cycle, u32 dwidth)
@@ -522,6 +615,21 @@ int vme_master_set(struct vme_resource *resource, int enabled,
}
EXPORT_SYMBOL(vme_master_set);
+/**
+ * vme_master_get - Retrieve VME master window configuration.
+ * @resource: Pointer to VME master resource.
+ * @enabled: Pointer to variable for storing state.
+ * @vme_base: Pointer to variable for storing window base address.
+ * @size: Pointer to variable for storing window size.
+ * @aspace: Pointer to variable for storing VME address space.
+ * @cycle: Pointer to variable for storing VME data transfer cycle type.
+ * @dwidth: Pointer to variable for storing VME data transfer width.
+ *
+ * Return configuration for provided VME master window.
+ *
+ * Return: Zero on success, -EINVAL if operation is not supported on this
+ * device or if an invalid resource has been provided.
+ */
int vme_master_get(struct vme_resource *resource, int *enabled,
unsigned long long *vme_base, unsigned long long *size, u32 *aspace,
u32 *cycle, u32 *dwidth)
@@ -546,8 +654,20 @@ int vme_master_get(struct vme_resource *resource, int *enabled,
}
EXPORT_SYMBOL(vme_master_get);
-/*
- * Read data out of VME space into a buffer.
+/**
+ * vme_master_write - Read data from VME space into a buffer.
+ * @resource: Pointer to VME master resource.
+ * @buf: Pointer to buffer where data should be transferred.
+ * @count: Number of bytes to transfer.
+ * @offset: Offset into VME master window at which to start transfer.
+ *
+ * Perform read of count bytes of data from location on VME bus which maps into
+ * the VME master window at offset to buf.
+ *
+ * Return: Number of bytes read, -EINVAL if resource is not a VME master
+ * resource or read operation is not supported. -EFAULT returned if
+ * invalid offset is provided. Hardware specific errors may also be
+ * returned.
*/
ssize_t vme_master_read(struct vme_resource *resource, void *buf, size_t count,
loff_t offset)
@@ -583,8 +703,20 @@ ssize_t vme_master_read(struct vme_resource *resource, void *buf, size_t count,
}
EXPORT_SYMBOL(vme_master_read);
-/*
- * Write data out to VME space from a buffer.
+/**
+ * vme_master_write - Write data out to VME space from a buffer.
+ * @resource: Pointer to VME master resource.
+ * @buf: Pointer to buffer holding data to transfer.
+ * @count: Number of bytes to transfer.
+ * @offset: Offset into VME master window at which to start transfer.
+ *
+ * Perform write of count bytes of data from buf to location on VME bus which
+ * maps into the VME master window at offset.
+ *
+ * Return: Number of bytes written, -EINVAL if resource is not a VME master
+ * resource or write operation is not supported. -EFAULT returned if
+ * invalid offset is provided. Hardware specific errors may also be
+ * returned.
*/
ssize_t vme_master_write(struct vme_resource *resource, void *buf,
size_t count, loff_t offset)
@@ -619,8 +751,24 @@ ssize_t vme_master_write(struct vme_resource *resource, void *buf,
}
EXPORT_SYMBOL(vme_master_write);
-/*
- * Perform RMW cycle to provided location.
+/**
+ * vme_master_rmw - Perform read-modify-write cycle.
+ * @resource: Pointer to VME master resource.
+ * @mask: Bits to be compared and swapped in operation.
+ * @compare: Bits to be compared with data read from offset.
+ * @swap: Bits to be swapped in data read from offset.
+ * @offset: Offset into VME master window at which to perform operation.
+ *
+ * Perform read-modify-write cycle on provided location:
+ * - Location on VME bus is read.
+ * - Bits selected by mask are compared with compare.
+ * - Where a selected bit matches that in compare and are selected in swap,
+ * the bit is swapped.
+ * - Result written back to location on VME bus.
+ *
+ * Return: Bytes written on success, -EINVAL if resource is not a VME master
+ * resource or RMW operation is not supported. Hardware specific
+ * errors may also be returned.
*/
unsigned int vme_master_rmw(struct vme_resource *resource, unsigned int mask,
unsigned int compare, unsigned int swap, loff_t offset)
@@ -644,6 +792,17 @@ unsigned int vme_master_rmw(struct vme_resource *resource, unsigned int mask,
}
EXPORT_SYMBOL(vme_master_rmw);
+/**
+ * vme_master_mmap - Mmap region of VME master window.
+ * @resource: Pointer to VME master resource.
+ * @vma: Pointer to definition of user mapping.
+ *
+ * Memory map a region of the VME master window into user space.
+ *
+ * Return: Zero on success, -EINVAL if resource is not a VME master
+ * resource or -EFAULT if map exceeds window size. Other generic mmap
+ * errors may also be returned.
+ */
int vme_master_mmap(struct vme_resource *resource, struct vm_area_struct *vma)
{
struct vme_master_resource *image;
@@ -670,6 +829,12 @@ int vme_master_mmap(struct vme_resource *resource, struct vm_area_struct *vma)
}
EXPORT_SYMBOL(vme_master_mmap);
+/**
+ * vme_master_free - Free VME master window
+ * @resource: Pointer to VME master resource.
+ *
+ * Free the provided master resource so that it may be reallocated.
+ */
void vme_master_free(struct vme_resource *resource)
{
struct vme_master_resource *master_image;
@@ -699,9 +864,15 @@ void vme_master_free(struct vme_resource *resource)
}
EXPORT_SYMBOL(vme_master_free);
-/*
- * Request a DMA controller with specific attributes, return some unique
- * identifier.
+/**
+ * vme_dma_request - Request a DMA controller.
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ * @route: Required src/destination combination.
+ *
+ * Request a VME DMA controller with capability to perform transfers bewteen
+ * requested source/destination combination.
+ *
+ * Return: Pointer to VME DMA resource on success, NULL on failure.
*/
struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route)
{
@@ -768,8 +939,15 @@ err_bus:
}
EXPORT_SYMBOL(vme_dma_request);
-/*
- * Start new list
+/**
+ * vme_new_dma_list - Create new VME DMA list.
+ * @resource: Pointer to VME DMA resource.
+ *
+ * Create a new VME DMA list. It is the responsibility of the user to free
+ * the list once it is no longer required with vme_dma_list_free().
+ *
+ * Return: Pointer to new VME DMA list, NULL on allocation failure or invalid
+ * VME DMA resource.
*/
struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
{
@@ -796,8 +974,16 @@ struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
}
EXPORT_SYMBOL(vme_new_dma_list);
-/*
- * Create "Pattern" type attributes
+/**
+ * vme_dma_pattern_attribute - Create "Pattern" type VME DMA list attribute.
+ * @pattern: Value to use used as pattern
+ * @type: Type of pattern to be written.
+ *
+ * Create VME DMA list attribute for pattern generation. It is the
+ * responsibility of the user to free used attributes using
+ * vme_dma_free_attribute().
+ *
+ * Return: Pointer to VME DMA attribute, NULL on failure.
*/
struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern, u32 type)
{
@@ -831,8 +1017,15 @@ err_attr:
}
EXPORT_SYMBOL(vme_dma_pattern_attribute);
-/*
- * Create "PCI" type attributes
+/**
+ * vme_dma_pci_attribute - Create "PCI" type VME DMA list attribute.
+ * @address: PCI base address for DMA transfer.
+ *
+ * Create VME DMA list attribute pointing to a location on PCI for DMA
+ * transfers. It is the responsibility of the user to free used attributes
+ * using vme_dma_free_attribute().
+ *
+ * Return: Pointer to VME DMA attribute, NULL on failure.
*/
struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address)
{
@@ -869,8 +1062,18 @@ err_attr:
}
EXPORT_SYMBOL(vme_dma_pci_attribute);
-/*
- * Create "VME" type attributes
+/**
+ * vme_dma_vme_attribute - Create "VME" type VME DMA list attribute.
+ * @address: VME base address for DMA transfer.
+ * @aspace: VME address space to use for DMA transfer.
+ * @cycle: VME bus cycle to use for DMA transfer.
+ * @dwidth: VME data width to use for DMA transfer.
+ *
+ * Create VME DMA list attribute pointing to a location on the VME bus for DMA
+ * transfers. It is the responsibility of the user to free used attributes
+ * using vme_dma_free_attribute().
+ *
+ * Return: Pointer to VME DMA attribute, NULL on failure.
*/
struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address,
u32 aspace, u32 cycle, u32 dwidth)
@@ -908,8 +1111,12 @@ err_attr:
}
EXPORT_SYMBOL(vme_dma_vme_attribute);
-/*
- * Free attribute
+/**
+ * vme_dma_free_attribute - Free DMA list attribute.
+ * @attributes: Pointer to DMA list attribute.
+ *
+ * Free VME DMA list attribute. VME DMA list attributes can be safely freed
+ * once vme_dma_list_add() has returned.
*/
void vme_dma_free_attribute(struct vme_dma_attr *attributes)
{
@@ -918,6 +1125,23 @@ void vme_dma_free_attribute(struct vme_dma_attr *attributes)
}
EXPORT_SYMBOL(vme_dma_free_attribute);
+/**
+ * vme_dma_list_add - Add enty to a VME DMA list.
+ * @list: Pointer to VME list.
+ * @src: Pointer to DMA list attribute to use as source.
+ * @dest: Pointer to DMA list attribute to use as destination.
+ * @count: Number of bytes to transfer.
+ *
+ * Add an entry to the provided VME DMA list. Entry requires pointers to source
+ * and destination DMA attributes and a count.
+ *
+ * Please note, the attributes supported as source and destinations for
+ * transfers are hardware dependent.
+ *
+ * Return: Zero on success, -EINVAL if operation is not supported on this
+ * device or if the link list has already been submitted for execution.
+ * Hardware specific errors also possible.
+ */
int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src,
struct vme_dma_attr *dest, size_t count)
{
@@ -942,6 +1166,16 @@ int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src,
}
EXPORT_SYMBOL(vme_dma_list_add);
+/**
+ * vme_dma_list_exec - Queue a VME DMA list for execution.
+ * @list: Pointer to VME list.
+ *
+ * Queue the provided VME DMA list for execution. The call will return once the
+ * list has been executed.
+ *
+ * Return: Zero on success, -EINVAL if operation is not supported on this
+ * device. Hardware specific errors also possible.
+ */
int vme_dma_list_exec(struct vme_dma_list *list)
{
struct vme_bridge *bridge = list->parent->parent;
@@ -962,6 +1196,15 @@ int vme_dma_list_exec(struct vme_dma_list *list)
}
EXPORT_SYMBOL(vme_dma_list_exec);
+/**
+ * vme_dma_list_free - Free a VME DMA list.
+ * @list: Pointer to VME list.
+ *
+ * Free the provided DMA list and all its entries.
+ *
+ * Return: Zero on success, -EINVAL on invalid VME resource, -EBUSY if resource
+ * is still in use. Hardware specific errors also possible.
+ */
int vme_dma_list_free(struct vme_dma_list *list)
{
struct vme_bridge *bridge = list->parent->parent;
@@ -994,6 +1237,15 @@ int vme_dma_list_free(struct vme_dma_list *list)
}
EXPORT_SYMBOL(vme_dma_list_free);
+/**
+ * vme_dma_free - Free a VME DMA resource.
+ * @resource: Pointer to VME DMA resource.
+ *
+ * Free the provided DMA resource so that it may be reallocated.
+ *
+ * Return: Zero on success, -EINVAL on invalid VME resource, -EBUSY if resource
+ * is still active.
+ */
int vme_dma_free(struct vme_resource *resource)
{
struct vme_dma_resource *ctrlr;
@@ -1099,6 +1351,22 @@ void vme_irq_handler(struct vme_bridge *bridge, int level, int statid)
}
EXPORT_SYMBOL(vme_irq_handler);
+/**
+ * vme_irq_request - Request a specific VME interrupt.
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ * @level: Interrupt priority being requested.
+ * @statid: Interrupt vector being requested.
+ * @callback: Pointer to callback function called when VME interrupt/vector
+ * received.
+ * @priv_data: Generic pointer that will be passed to the callback function.
+ *
+ * Request callback to be attached as a handler for VME interrupts with provided
+ * level and statid.
+ *
+ * Return: Zero on success, -EINVAL on invalid vme device, level or if the
+ * function is not supported, -EBUSY if the level/statid combination is
+ * already in use. Hardware specific errors also possible.
+ */
int vme_irq_request(struct vme_dev *vdev, int level, int statid,
void (*callback)(int, int, void *),
void *priv_data)
@@ -1142,6 +1410,14 @@ int vme_irq_request(struct vme_dev *vdev, int level, int statid,
}
EXPORT_SYMBOL(vme_irq_request);
+/**
+ * vme_irq_free - Free a VME interrupt.
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ * @level: Interrupt priority of interrupt being freed.
+ * @statid: Interrupt vector of interrupt being freed.
+ *
+ * Remove previously attached callback from VME interrupt priority/vector.
+ */
void vme_irq_free(struct vme_dev *vdev, int level, int statid)
{
struct vme_bridge *bridge;
@@ -1177,6 +1453,18 @@ void vme_irq_free(struct vme_dev *vdev, int level, int statid)
}
EXPORT_SYMBOL(vme_irq_free);
+/**
+ * vme_irq_generate - Generate VME interrupt.
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ * @level: Interrupt priority at which to assert the interrupt.
+ * @statid: Interrupt vector to associate with the interrupt.
+ *
+ * Generate a VME interrupt of the provided level and with the provided
+ * statid.
+ *
+ * Return: Zero on success, -EINVAL on invalid vme device, level or if the
+ * function is not supported. Hardware specific errors also possible.
+ */
int vme_irq_generate(struct vme_dev *vdev, int level, int statid)
{
struct vme_bridge *bridge;
@@ -1201,8 +1489,15 @@ int vme_irq_generate(struct vme_dev *vdev, int level, int statid)
}
EXPORT_SYMBOL(vme_irq_generate);
-/*
- * Request the location monitor, return resource or NULL
+/**
+ * vme_lm_request - Request a VME location monitor
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ *
+ * Allocate a location monitor resource to the driver. A location monitor
+ * allows the driver to monitor accesses to a contiguous number of
+ * addresses on the VME bus.
+ *
+ * Return: Pointer to a VME resource on success or NULL on failure.
*/
struct vme_resource *vme_lm_request(struct vme_dev *vdev)
{
@@ -1218,7 +1513,7 @@ struct vme_resource *vme_lm_request(struct vme_dev *vdev)
goto err_bus;
}
- /* Loop through DMA resources */
+ /* Loop through LM resources */
list_for_each(lm_pos, &bridge->lm_resources) {
lm = list_entry(lm_pos,
struct vme_lm_resource, list);
@@ -1264,6 +1559,17 @@ err_bus:
}
EXPORT_SYMBOL(vme_lm_request);
+/**
+ * vme_lm_count - Determine number of VME Addresses monitored
+ * @resource: Pointer to VME location monitor resource.
+ *
+ * The number of contiguous addresses monitored is hardware dependent.
+ * Return the number of contiguous addresses monitored by the
+ * location monitor.
+ *
+ * Return: Count of addresses monitored or -EINVAL when provided with an
+ * invalid location monitor resource.
+ */
int vme_lm_count(struct vme_resource *resource)
{
struct vme_lm_resource *lm;
@@ -1279,6 +1585,20 @@ int vme_lm_count(struct vme_resource *resource)
}
EXPORT_SYMBOL(vme_lm_count);
+/**
+ * vme_lm_set - Configure location monitor
+ * @resource: Pointer to VME location monitor resource.
+ * @lm_base: Base address to monitor.
+ * @aspace: VME address space to monitor.
+ * @cycle: VME bus cycle type to monitor.
+ *
+ * Set the base address, address space and cycle type of accesses to be
+ * monitored by the location monitor.
+ *
+ * Return: Zero on success, -EINVAL when provided with an invalid location
+ * monitor resource or function is not supported. Hardware specific
+ * errors may also be returned.
+ */
int vme_lm_set(struct vme_resource *resource, unsigned long long lm_base,
u32 aspace, u32 cycle)
{
@@ -1301,6 +1621,20 @@ int vme_lm_set(struct vme_resource *resource, unsigned long long lm_base,
}
EXPORT_SYMBOL(vme_lm_set);
+/**
+ * vme_lm_get - Retrieve location monitor settings
+ * @resource: Pointer to VME location monitor resource.
+ * @lm_base: Pointer used to output the base address monitored.
+ * @aspace: Pointer used to output the address space monitored.
+ * @cycle: Pointer used to output the VME bus cycle type monitored.
+ *
+ * Retrieve the base address, address space and cycle type of accesses to
+ * be monitored by the location monitor.
+ *
+ * Return: Zero on success, -EINVAL when provided with an invalid location
+ * monitor resource or function is not supported. Hardware specific
+ * errors may also be returned.
+ */
int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base,
u32 *aspace, u32 *cycle)
{
@@ -1323,6 +1657,21 @@ int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base,
}
EXPORT_SYMBOL(vme_lm_get);
+/**
+ * vme_lm_attach - Provide callback for location monitor address
+ * @resource: Pointer to VME location monitor resource.
+ * @monitor: Offset to which callback should be attached.
+ * @callback: Pointer to callback function called when triggered.
+ * @data: Generic pointer that will be passed to the callback function.
+ *
+ * Attach a callback to the specificed offset into the location monitors
+ * monitored addresses. A generic pointer is provided to allow data to be
+ * passed to the callback when called.
+ *
+ * Return: Zero on success, -EINVAL when provided with an invalid location
+ * monitor resource or function is not supported. Hardware specific
+ * errors may also be returned.
+ */
int vme_lm_attach(struct vme_resource *resource, int monitor,
void (*callback)(void *), void *data)
{
@@ -1345,6 +1694,18 @@ int vme_lm_attach(struct vme_resource *resource, int monitor,
}
EXPORT_SYMBOL(vme_lm_attach);
+/**
+ * vme_lm_detach - Remove callback for location monitor address
+ * @resource: Pointer to VME location monitor resource.
+ * @monitor: Offset to which callback should be removed.
+ *
+ * Remove the callback associated with the specificed offset into the
+ * location monitors monitored addresses.
+ *
+ * Return: Zero on success, -EINVAL when provided with an invalid location
+ * monitor resource or function is not supported. Hardware specific
+ * errors may also be returned.
+ */
int vme_lm_detach(struct vme_resource *resource, int monitor)
{
struct vme_bridge *bridge = find_bridge(resource);
@@ -1366,6 +1727,18 @@ int vme_lm_detach(struct vme_resource *resource, int monitor)
}
EXPORT_SYMBOL(vme_lm_detach);
+/**
+ * vme_lm_free - Free allocated VME location monitor
+ * @resource: Pointer to VME location monitor resource.
+ *
+ * Free allocation of a VME location monitor.
+ *
+ * WARNING: This function currently expects that any callbacks that have
+ * been attached to the location monitor have been removed.
+ *
+ * Return: Zero on success, -EINVAL when provided with an invalid location
+ * monitor resource.
+ */
void vme_lm_free(struct vme_resource *resource)
{
struct vme_lm_resource *lm;
@@ -1392,6 +1765,16 @@ void vme_lm_free(struct vme_resource *resource)
}
EXPORT_SYMBOL(vme_lm_free);
+/**
+ * vme_slot_num - Retrieve slot ID
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ *
+ * Retrieve the slot ID associated with the provided VME device.
+ *
+ * Return: The slot ID on success, -EINVAL if VME bridge cannot be determined
+ * or the function is not supported. Hardware specific errors may also
+ * be returned.
+ */
int vme_slot_num(struct vme_dev *vdev)
{
struct vme_bridge *bridge;
@@ -1411,6 +1794,15 @@ int vme_slot_num(struct vme_dev *vdev)
}
EXPORT_SYMBOL(vme_slot_num);
+/**
+ * vme_bus_num - Retrieve bus number
+ * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
+ *
+ * Retrieve the bus enumeration associated with the provided VME device.
+ *
+ * Return: The bus number on success, -EINVAL if VME bridge cannot be
+ * determined.
+ */
int vme_bus_num(struct vme_dev *vdev)
{
struct vme_bridge *bridge;
@@ -1556,6 +1948,15 @@ static int __vme_register_driver(struct vme_driver *drv, unsigned int ndevs)
return err;
}
+/**
+ * vme_register_driver - Register a VME driver
+ * @drv: Pointer to VME driver structure to register.
+ * @ndevs: Maximum number of devices to allow to be enumerated.
+ *
+ * Register a VME device driver with the VME subsystem.
+ *
+ * Return: Zero on success, error value on registration failure.
+ */
int vme_register_driver(struct vme_driver *drv, unsigned int ndevs)
{
int err;
@@ -1576,6 +1977,12 @@ int vme_register_driver(struct vme_driver *drv, unsigned int ndevs)
}
EXPORT_SYMBOL(vme_register_driver);
+/**
+ * vme_unregister_driver - Unregister a VME driver
+ * @drv: Pointer to VME driver structure to unregister.
+ *
+ * Unregister a VME device driver from the VME subsystem.
+ */
void vme_unregister_driver(struct vme_driver *drv)
{
struct vme_dev *dev, *dev_tmp;
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index 0ef9f2663dbd..fb68465908f2 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -86,6 +86,12 @@ config W1_SLAVE_DS2433_CRC
Each block has 30 bytes of data and a two byte CRC16.
Full block writes are only allowed if the CRC is valid.
+config W1_SLAVE_DS2438
+ tristate "DS2438 Smart Battery Monitor 0x26 family support"
+ help
+ Say Y here if you want to use a 1-wire
+ DS2438 Smart Battery Monitor device support
+
config W1_SLAVE_DS2760
tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)"
help
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index b4a358955ef9..54c63e420302 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o
obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o
obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
+obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o
obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o
diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c
new file mode 100644
index 000000000000..5ededb4965e1
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2438.c
@@ -0,0 +1,390 @@
+/*
+ * 1-Wire implementation for the ds2438 chip
+ *
+ * Copyright (c) 2017 Mariusz Bialonczyk <manio@skyboo.net>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include "../w1.h"
+#include "../w1_family.h"
+
+#define W1_DS2438_RETRIES 3
+
+/* Memory commands */
+#define W1_DS2438_READ_SCRATCH 0xBE
+#define W1_DS2438_WRITE_SCRATCH 0x4E
+#define W1_DS2438_COPY_SCRATCH 0x48
+#define W1_DS2438_RECALL_MEMORY 0xB8
+/* Register commands */
+#define W1_DS2438_CONVERT_TEMP 0x44
+#define W1_DS2438_CONVERT_VOLTAGE 0xB4
+
+#define DS2438_PAGE_SIZE 8
+#define DS2438_ADC_INPUT_VAD 0
+#define DS2438_ADC_INPUT_VDD 1
+#define DS2438_MAX_CONVERSION_TIME 10 /* ms */
+
+/* Page #0 definitions */
+#define DS2438_STATUS_REG 0x00 /* Status/Configuration Register */
+#define DS2438_STATUS_IAD (1 << 0) /* Current A/D Control Bit */
+#define DS2438_STATUS_CA (1 << 1) /* Current Accumulator Configuration */
+#define DS2438_STATUS_EE (1 << 2) /* Current Accumulator Shadow Selector bit */
+#define DS2438_STATUS_AD (1 << 3) /* Voltage A/D Input Select Bit */
+#define DS2438_STATUS_TB (1 << 4) /* Temperature Busy Flag */
+#define DS2438_STATUS_NVB (1 << 5) /* Nonvolatile Memory Busy Flag */
+#define DS2438_STATUS_ADB (1 << 6) /* A/D Converter Busy Flag */
+
+#define DS2438_TEMP_LSB 0x01
+#define DS2438_TEMP_MSB 0x02
+#define DS2438_VOLTAGE_LSB 0x03
+#define DS2438_VOLTAGE_MSB 0x04
+#define DS2438_CURRENT_LSB 0x05
+#define DS2438_CURRENT_MSB 0x06
+#define DS2438_THRESHOLD 0x07
+
+int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[2];
+ u8 crc;
+ size_t count;
+
+ while (retries--) {
+ crc = 0;
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_RECALL_MEMORY;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_READ_SCRATCH;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1);
+ if (count == DS2438_PAGE_SIZE + 1) {
+ crc = w1_calc_crc8(buf, DS2438_PAGE_SIZE);
+
+ /* check for correct CRC */
+ if ((u8)buf[DS2438_PAGE_SIZE] == crc)
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+ unsigned int tm = DS2438_MAX_CONVERSION_TIME;
+ unsigned long sleep_rem;
+ int ret;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP);
+
+ mutex_unlock(&sl->master->bus_mutex);
+ sleep_rem = msleep_interruptible(tm);
+ if (sleep_rem != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ break;
+ }
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ *temperature = (((int16_t) w1_buf[DS2438_TEMP_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_TEMP_LSB]);
+ ret = 0;
+ } else
+ ret = -1;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+post_unlock:
+ return ret;
+}
+
+int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[3];
+ u8 status;
+ int perform_write = 0;
+
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_RECALL_MEMORY;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_READ_SCRATCH;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ /* reading one byte of result */
+ status = w1_read_8(sl->master);
+
+ /* if bit0=1, set a value to a mask for easy compare */
+ if (value)
+ value = mask;
+
+ if ((status & mask) == value)
+ return 0; /* already set as requested */
+ else {
+ /* changing bit */
+ status ^= mask;
+ perform_write = 1;
+ }
+ break;
+ }
+
+ if (perform_write) {
+ retries = W1_DS2438_RETRIES;
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
+ w1_buf[1] = 0x00;
+ w1_buf[2] = status;
+ w1_write_block(sl->master, w1_buf, 3);
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_COPY_SCRATCH;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ return 0;
+ }
+ }
+ return -1;
+}
+
+uint16_t w1_ds2438_get_voltage(struct w1_slave *sl, int adc_input, uint16_t *voltage)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+ unsigned int tm = DS2438_MAX_CONVERSION_TIME;
+ unsigned long sleep_rem;
+ int ret;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) {
+ ret = -1;
+ goto pre_unlock;
+ }
+
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE);
+
+ mutex_unlock(&sl->master->bus_mutex);
+ sleep_rem = msleep_interruptible(tm);
+ if (sleep_rem != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ break;
+ }
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ *voltage = (((uint16_t) w1_buf[DS2438_VOLTAGE_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_VOLTAGE_LSB]);
+ ret = 0;
+ } else
+ ret = -1;
+
+pre_unlock:
+ mutex_unlock(&sl->master->bus_mutex);
+
+post_unlock:
+ return ret;
+}
+
+static ssize_t iad_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0)
+ ret = 1;
+ else
+ ret = -EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
+static ssize_t page0_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ memcpy(buf, &w1_buf, DS2438_PAGE_SIZE);
+ ret = DS2438_PAGE_SIZE;
+ } else
+ ret = -EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
+static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ ssize_t c = PAGE_SIZE;
+ int16_t temp;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_temperature(sl, &temp) == 0) {
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", temp);
+ ret = PAGE_SIZE - c;
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+static ssize_t vad_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ ssize_t c = PAGE_SIZE;
+ uint16_t voltage;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) {
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage);
+ ret = PAGE_SIZE - c;
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ ssize_t c = PAGE_SIZE;
+ uint16_t voltage;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) {
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage);
+ ret = PAGE_SIZE - c;
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, NULL, iad_write, 1);
+static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
+static BIN_ATTR_RO(temperature, 0/* real length varies */);
+static BIN_ATTR_RO(vad, 0/* real length varies */);
+static BIN_ATTR_RO(vdd, 0/* real length varies */);
+
+static struct bin_attribute *w1_ds2438_bin_attrs[] = {
+ &bin_attr_iad,
+ &bin_attr_page0,
+ &bin_attr_temperature,
+ &bin_attr_vad,
+ &bin_attr_vdd,
+ NULL,
+};
+
+static const struct attribute_group w1_ds2438_group = {
+ .bin_attrs = w1_ds2438_bin_attrs,
+};
+
+static const struct attribute_group *w1_ds2438_groups[] = {
+ &w1_ds2438_group,
+ NULL,
+};
+
+static struct w1_family_ops w1_ds2438_fops = {
+ .groups = w1_ds2438_groups,
+};
+
+static struct w1_family w1_ds2438_family = {
+ .fid = W1_FAMILY_DS2438,
+ .fops = &w1_ds2438_fops,
+};
+module_w1_family(w1_ds2438_family);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
+MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438));
diff --git a/drivers/w1/slaves/w1_ds2760.h b/drivers/w1/slaves/w1_ds2760.h
index 58e774141568..24168c94eeae 100644
--- a/drivers/w1/slaves/w1_ds2760.h
+++ b/drivers/w1/slaves/w1_ds2760.h
@@ -24,11 +24,13 @@
#define DS2760_DATA_SIZE 0x40
#define DS2760_PROTECTION_REG 0x00
+
#define DS2760_STATUS_REG 0x01
- #define DS2760_STATUS_IE (1 << 2)
- #define DS2760_STATUS_SWEN (1 << 3)
- #define DS2760_STATUS_RNAOP (1 << 4)
- #define DS2760_STATUS_PMOD (1 << 5)
+#define DS2760_STATUS_IE (1 << 2)
+#define DS2760_STATUS_SWEN (1 << 3)
+#define DS2760_STATUS_RNAOP (1 << 4)
+#define DS2760_STATUS_PMOD (1 << 5)
+
#define DS2760_EEPROM_REG 0x07
#define DS2760_SPECIAL_FEATURE_REG 0x08
#define DS2760_VOLTAGE_MSB 0x0c
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index c4a6b257a367..869a3ff87d29 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -29,6 +29,7 @@
#define W1_COUNTER_DS2423 0x1D
#define W1_THERM_DS1822 0x22
#define W1_EEPROM_DS2433 0x23
+#define W1_FAMILY_DS2438 0x26
#define W1_THERM_DS18B20 0x28
#define W1_FAMILY_DS2408 0x29
#define W1_EEPROM_DS2431 0x2D
diff --git a/drivers/zorro/zorro-driver.c b/drivers/zorro/zorro-driver.c
index eacae1434b73..fa23b7366b98 100644
--- a/drivers/zorro/zorro-driver.c
+++ b/drivers/zorro/zorro-driver.c
@@ -14,6 +14,8 @@
#include <linux/module.h>
#include <linux/zorro.h>
+#include "zorro.h"
+
/**
* zorro_match_device - Tell if a Zorro device structure has a matching
@@ -161,12 +163,13 @@ static int zorro_uevent(struct device *dev, struct kobj_uevent_env *env)
}
struct bus_type zorro_bus_type = {
- .name = "zorro",
- .dev_name = "zorro",
- .match = zorro_bus_match,
- .uevent = zorro_uevent,
- .probe = zorro_device_probe,
- .remove = zorro_device_remove,
+ .name = "zorro",
+ .dev_name = "zorro",
+ .dev_groups = zorro_device_attribute_groups,
+ .match = zorro_bus_match,
+ .uevent = zorro_uevent,
+ .probe = zorro_device_probe,
+ .remove = zorro_device_remove,
};
EXPORT_SYMBOL(zorro_bus_type);
diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c
index 9282dbf5abdb..3d34dba9bb2d 100644
--- a/drivers/zorro/zorro-sysfs.c
+++ b/drivers/zorro/zorro-sysfs.c
@@ -23,33 +23,33 @@
/* show configuration fields */
#define zorro_config_attr(name, field, format_string) \
-static ssize_t \
-show_##name(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
{ \
struct zorro_dev *z; \
\
z = to_zorro_dev(dev); \
return sprintf(buf, format_string, z->field); \
} \
-static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+static DEVICE_ATTR_RO(name);
zorro_config_attr(id, id, "0x%08x\n");
zorro_config_attr(type, rom.er_Type, "0x%02x\n");
zorro_config_attr(slotaddr, slotaddr, "0x%04x\n");
zorro_config_attr(slotsize, slotsize, "0x%04x\n");
-static ssize_t
-show_serial(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct zorro_dev *z;
z = to_zorro_dev(dev);
return sprintf(buf, "0x%08x\n", be32_to_cpu(z->rom.er_SerialNumber));
}
+static DEVICE_ATTR_RO(serial);
-static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL);
-
-static ssize_t zorro_show_resource(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct zorro_dev *z = to_zorro_dev(dev);
@@ -58,8 +58,27 @@ static ssize_t zorro_show_resource(struct device *dev, struct device_attribute *
(unsigned long)zorro_resource_end(z),
zorro_resource_flags(z));
}
+static DEVICE_ATTR_RO(resource);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct zorro_dev *z = to_zorro_dev(dev);
-static DEVICE_ATTR(resource, S_IRUGO, zorro_show_resource, NULL);
+ return sprintf(buf, ZORRO_DEVICE_MODALIAS_FMT "\n", z->id);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *zorro_device_attrs[] = {
+ &dev_attr_id.attr,
+ &dev_attr_type.attr,
+ &dev_attr_serial.attr,
+ &dev_attr_slotaddr.attr,
+ &dev_attr_slotsize.attr,
+ &dev_attr_resource.attr,
+ &dev_attr_modalias.attr,
+ NULL
+};
static ssize_t zorro_read_config(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
@@ -88,32 +107,17 @@ static struct bin_attribute zorro_config_attr = {
.read = zorro_read_config,
};
-static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct zorro_dev *z = to_zorro_dev(dev);
-
- return sprintf(buf, ZORRO_DEVICE_MODALIAS_FMT "\n", z->id);
-}
-
-static DEVICE_ATTR(modalias, S_IRUGO, modalias_show, NULL);
+static struct bin_attribute *zorro_device_bin_attrs[] = {
+ &zorro_config_attr,
+ NULL
+};
-int zorro_create_sysfs_dev_files(struct zorro_dev *z)
-{
- struct device *dev = &z->dev;
- int error;
-
- /* current configuration's attributes */
- if ((error = device_create_file(dev, &dev_attr_id)) ||
- (error = device_create_file(dev, &dev_attr_type)) ||
- (error = device_create_file(dev, &dev_attr_serial)) ||
- (error = device_create_file(dev, &dev_attr_slotaddr)) ||
- (error = device_create_file(dev, &dev_attr_slotsize)) ||
- (error = device_create_file(dev, &dev_attr_resource)) ||
- (error = device_create_file(dev, &dev_attr_modalias)) ||
- (error = sysfs_create_bin_file(&dev->kobj, &zorro_config_attr)))
- return error;
-
- return 0;
-}
+static const struct attribute_group zorro_device_attr_group = {
+ .attrs = zorro_device_attrs,
+ .bin_attrs = zorro_device_bin_attrs,
+};
+const struct attribute_group *zorro_device_attribute_groups[] = {
+ &zorro_device_attr_group,
+ NULL
+};
diff --git a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c
index d295d9878dff..cc1b1ac57d61 100644
--- a/drivers/zorro/zorro.c
+++ b/drivers/zorro/zorro.c
@@ -197,9 +197,6 @@ static int __init amiga_zorro_probe(struct platform_device *pdev)
put_device(&z->dev);
continue;
}
- error = zorro_create_sysfs_dev_files(z);
- if (error)
- dev_err(&z->dev, "Error creating sysfs files\n");
}
/* Mark all available Zorro II memory */
diff --git a/drivers/zorro/zorro.h b/drivers/zorro/zorro.h
index 34119fb4e560..4f805c01cfbc 100644
--- a/drivers/zorro/zorro.h
+++ b/drivers/zorro/zorro.h
@@ -5,5 +5,4 @@ extern void zorro_name_device(struct zorro_dev *z);
static inline void zorro_name_device(struct zorro_dev *dev) { }
#endif
-extern int zorro_create_sysfs_dev_files(struct zorro_dev *z);
-
+extern const struct attribute_group *zorro_device_attribute_groups[];