summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/led/Kconfig21
-rw-r--r--drivers/led/Makefile3
-rw-r--r--drivers/led/core.c157
-rw-r--r--drivers/led/led-gpio.c94
-rw-r--r--drivers/led/led-triggers.c153
-rw-r--r--drivers/mci/Kconfig4
-rw-r--r--drivers/mci/stm378x.c143
-rw-r--r--drivers/net/fec_imx.c95
-rw-r--r--drivers/net/fec_imx.h23
-rw-r--r--drivers/net/netx_eth.c1
-rw-r--r--drivers/nor/cfi_flash.c143
-rw-r--r--drivers/nor/cfi_flash.h25
-rw-r--r--drivers/serial/Kconfig2
-rw-r--r--drivers/serial/stm-serial.c9
-rw-r--r--drivers/spi/imx_spi.c3
-rw-r--r--drivers/video/Kconfig7
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/fb.c44
-rw-r--r--drivers/video/stm.c540
21 files changed, 1311 insertions, 159 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d94017bfc1..86d8fb54cc 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -12,5 +12,6 @@ source "drivers/video/Kconfig"
source "drivers/mci/Kconfig"
source "drivers/clk/Kconfig"
source "drivers/mfd/Kconfig"
+source "drivers/led/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 242a564dd7..b1b402fb49 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_MCI) += mci/
obj-$(CONFIG_VIDEO) += video/
obj-y += clk/
obj-y += mfd/
+obj-$(CONFIG_LED) += led/
diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig
new file mode 100644
index 0000000000..106093b722
--- /dev/null
+++ b/drivers/led/Kconfig
@@ -0,0 +1,21 @@
+menuconfig LED
+ bool "LED support"
+
+if LED
+
+config LED_GPIO
+ bool "gpio LED support"
+ depends on GENERIC_GPIO
+
+config LED_GPIO_RGB
+ bool "gpio rgb LED support"
+ depends on LED_GPIO
+
+config LED_TRIGGERS
+ select POLLER
+ bool "LED triggers support"
+ help
+ This allows to assign certain triggers like heartbeat or network
+ activity to LEDs.
+
+endif
diff --git a/drivers/led/Makefile b/drivers/led/Makefile
new file mode 100644
index 0000000000..6aafa6df05
--- /dev/null
+++ b/drivers/led/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LED) += core.o
+obj-$(CONFIG_LED_GPIO) += led-gpio.o
+obj-$(CONFIG_LED_TRIGGERS) += led-triggers.o
diff --git a/drivers/led/core.c b/drivers/led/core.c
new file mode 100644
index 0000000000..748a978b98
--- /dev/null
+++ b/drivers/led/core.c
@@ -0,0 +1,157 @@
+/*
+ * core LED support for barebox
+ *
+ * (C) Copyright 2010 Sascha Hauer, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <command.h>
+#include <linux/list.h>
+#include <errno.h>
+#include <asm/gpio.h>
+#include <led.h>
+#include <poller.h>
+#include <clock.h>
+#include <linux/ctype.h>
+
+/**
+ * @file
+ * @brief LED framework
+ *
+ * This file contains the core LED framework for barebox.
+ *
+ * Each LED can be set to a value where 0 means disabled and values
+ * > 0 mean enabled. LEDs can have different enable values where the
+ * exact meaning depends on the LED, for example a gpio controlled rgb
+ * LED can have enable values from 1 to 7 which correspond to different
+ * colors. value could also mean a brightness.
+ * Each LED is assigned a number. numbers start with 0 and are increased
+ * with each registered LED. The number stays the same during lifecycle,
+ * gaps because of unregistered LEDs are not filled up.
+ */
+
+static LIST_HEAD(leds);
+static int num_leds;
+
+/**
+ * led_by_number - get the number of a LED
+ * @param num number of the LED to return
+ */
+struct led *led_by_number(int num)
+{
+ struct led *led;
+
+ list_for_each_entry(led, &leds, list) {
+ if (led->num == num)
+ return led;
+ }
+
+ return NULL;
+}
+
+/**
+ * led_by_name - get a LED with its name
+ * @param name name of the LED
+ */
+struct led *led_by_name(const char *name)
+{
+ struct led *led;
+
+ list_for_each_entry(led, &leds, list) {
+ if (led->name && !strcmp(led->name, name))
+ return led;
+ }
+
+ return NULL;
+}
+
+/**
+ * led_by_name_or_number - get a LED with its name or number
+ * @param str if first character of str is a digit led_by_number
+ * is returned, led_by_name otherwise.
+ */
+struct led *led_by_name_or_number(const char *str)
+{
+ if (isdigit(*str)) {
+ int l;
+
+ l = simple_strtoul(str, NULL, 0);
+
+ return led_by_number(l);
+ } else {
+ return led_by_name(str);
+ }
+}
+
+/**
+ * led_set - set the value of a LED
+ * @param led the led
+ * @param value the value of the LED (0 is disabled)
+ */
+int led_set(struct led *led, unsigned int value)
+{
+ if (value > led->max_value)
+ value = led->max_value;
+
+ led->set(led, value);
+
+ return 0;
+}
+
+/**
+ * led_set_num - set the value of a LED
+ * @param num the number of the LED
+ * @param value the value of the LED (0 is disabled)
+ */
+int led_set_num(int num, unsigned int value)
+{
+ struct led *led = led_by_number(num);
+
+ if (!led)
+ return -ENODEV;
+
+ return led_set(led, value);
+}
+
+/**
+ * led_register - Register a LED
+ * @param led the led
+ */
+int led_register(struct led *led)
+{
+ if (led->name && led_by_name(led->name))
+ return -EBUSY;
+
+ led->num = num_leds++;
+
+ list_add_tail(&led->list, &leds);
+
+ return 0;
+}
+
+/**
+ * led_unregister - Unegister a LED
+ * @param led the led
+ */
+void led_unregister(struct led *led)
+{
+ list_del(&led->list);
+}
diff --git a/drivers/led/led-gpio.c b/drivers/led/led-gpio.c
new file mode 100644
index 0000000000..10c25c6fbb
--- /dev/null
+++ b/drivers/led/led-gpio.c
@@ -0,0 +1,94 @@
+/*
+ * gpio LED support for barebox
+ *
+ * (C) Copyright 2010 Sascha Hauer, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include <common.h>
+#include <led.h>
+#include <asm/gpio.h>
+
+static void led_gpio_set(struct led *led, unsigned int value)
+{
+ struct gpio_led *gpio_led = container_of(led, struct gpio_led, led);
+
+ gpio_direction_output(gpio_led->gpio, !!value ^ gpio_led->active_low);
+}
+
+/**
+ * led_gpio_register - register a gpio controlled LED
+ * @param led The gpio LED
+ *
+ * This function registers a single gpio as a LED. led->gpio
+ * should be initialized to the gpio to control.
+ */
+int led_gpio_register(struct gpio_led *led)
+{
+ led->led.set = led_gpio_set;
+ led->led.max_value = 1;
+
+ return led_register(&led->led);
+}
+
+/**
+ * led_gpio_unregister - remove a gpio controlled LED from the framework
+ * @param led The gpio LED
+ */
+void led_gpio_unregister(struct gpio_led *led)
+{
+ led_unregister(&led->led);
+}
+
+#ifdef CONFIG_LED_GPIO_RGB
+
+static void led_gpio_rgb_set(struct led *led, unsigned int value)
+{
+ struct gpio_rgb_led *rgb = container_of(led, struct gpio_rgb_led, led);
+ int al = rgb->active_low;
+
+ gpio_direction_output(rgb->gpio_r, !!(value & 4) ^ al);
+ gpio_direction_output(rgb->gpio_g, !!(value & 2) ^ al);
+ gpio_direction_output(rgb->gpio_b, !!(value & 1) ^ al);
+}
+
+/**
+ * led_gpio_rgb_register - register three gpios as a rgb LED
+ * @param led The gpio rg LED
+ *
+ * This function registers three gpios as a rgb LED. led->gpio[rgb]
+ * should be initialized to the gpios to control.
+ */
+int led_gpio_rgb_register(struct gpio_rgb_led *led)
+{
+ led->led.set = led_gpio_rgb_set;
+ led->led.max_value = 7;
+
+ return led_register(&led->led);
+}
+
+/**
+ * led_gpio_rgb_unregister - remove a gpio controlled rgb LED from the framework
+ * @param led The gpio LED
+ */
+void led_gpio_rgb_unregister(struct gpio_led *led)
+{
+ led_unregister(&led->led);
+}
+#endif /* CONFIG_LED_GPIO_RGB */
diff --git a/drivers/led/led-triggers.c b/drivers/led/led-triggers.c
new file mode 100644
index 0000000000..764b4e79dc
--- /dev/null
+++ b/drivers/led/led-triggers.c
@@ -0,0 +1,153 @@
+/*
+ * LED trigger support for barebox
+ *
+ * (C) Copyright 2010 Sascha Hauer, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <poller.h>
+#include <errno.h>
+#include <clock.h>
+#include <led.h>
+#include <init.h>
+
+/**
+ * @file
+ * @brief LED trigger framework
+ *
+ * This file contains triggers which can be associated to LEDs.
+ *
+ * With this framework LEDs can be associated to different events.
+ * An event can be a heartbeat, network activity or panic.
+ * led_trigger() is the central function which is called in the
+ * different barebox frameworks to trigger an event.
+ *
+ * currently there are the following triggers are defined:
+ *
+ * led_trigger_panic: triggered in panic()
+ * led_trigger_heartbeat: shows the heartbeat of barebox. Blinks as long
+ * barebox is up and running.
+ * led_trigger_net_rx: Triggered during network packet reception
+ * led_trigger_net_tx: Triggered during network packet transmission
+ * led_trigger_net_txrx: combination of the two above
+ */
+
+struct led_trigger_struct {
+ struct led *led;
+ uint64_t flash_start;
+};
+
+static struct led_trigger_struct triggers[LED_TRIGGER_MAX];
+
+static void trigger_func(struct poller_struct *poller)
+{
+ int i;
+
+ for (i = 0; i < LED_TRIGGER_MAX; i++) {
+ if (triggers[i].led &&
+ triggers[i].flash_start &&
+ is_timeout(triggers[i].flash_start, 200 * MSECOND)) {
+ led_set(triggers[i].led, 0);
+ }
+ }
+
+ if (triggers[LED_TRIGGER_HEARTBEAT].led &&
+ is_timeout(triggers[LED_TRIGGER_HEARTBEAT].flash_start, SECOND))
+ led_trigger(LED_TRIGGER_HEARTBEAT, TRIGGER_FLASH);
+}
+
+static struct poller_struct trigger_poller = {
+ .func = trigger_func,
+};
+
+/**
+ * led_trigger - triggers a trigger
+ * @param trigger The trigger to enable/disable
+ * @param enable true if enable
+ *
+ * Enable/disable a LED for a given trigger.
+ */
+void led_trigger(enum led_trigger trigger, enum trigger_type type)
+{
+ if (trigger >= LED_TRIGGER_MAX)
+ return;
+ if (!triggers[trigger].led)
+ return;
+
+ if (type == TRIGGER_FLASH) {
+ if (is_timeout(triggers[trigger].flash_start, 400 * MSECOND)) {
+ led_set(triggers[trigger].led, 1);
+ triggers[trigger].flash_start = get_time_ns();
+ }
+ return;
+ }
+
+ led_set(triggers[trigger].led, type == TRIGGER_ENABLE ? 1 : 0);
+}
+
+/**
+ * led_set_trigger - set the LED for a trigger
+ * @param trigger The trigger to set a LED for
+ * @param led The LED
+ *
+ * This function associates a trigger with a LED. Pass led = NULL
+ * to disable a trigger
+ */
+int led_set_trigger(enum led_trigger trigger, struct led *led)
+{
+ int i;
+
+ if (trigger >= LED_TRIGGER_MAX)
+ return -EINVAL;
+
+ if (led)
+ for (i = 0; i < LED_TRIGGER_MAX; i++)
+ if (triggers[i].led == led)
+ return -EBUSY;
+
+ if (triggers[trigger].led && !led)
+ led_set(triggers[trigger].led, 0);
+
+ triggers[trigger].led = led;
+
+ return 0;
+}
+
+/**
+ * led_get_trigger - get the LED for a trigger
+ * @param trigger The trigger to set a LED for
+ *
+ * return the LED number of a trigger.
+ */
+int led_get_trigger(enum led_trigger trigger)
+{
+ if (trigger >= LED_TRIGGER_MAX)
+ return -EINVAL;
+ if (!triggers[trigger].led)
+ return -ENODEV;
+ return led_get_number(triggers[trigger].led);
+}
+
+int trigger_init(void)
+{
+ return poller_register(&trigger_poller);
+}
+late_initcall(trigger_init);
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index b1f2773354..ee04079c38 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -28,11 +28,11 @@ config MCI_INFO
comment "--- MCI host drivers ---"
config MCI_STM378X
- bool "i.MX23"
+ bool "i.MX23/i.MX28"
depends on ARCH_STM
help
Enable this entry to add support to read and write SD cards on a
- i.MX23 based system.
+ i.MX23/i.MX28 based system.
config MCI_S3C
bool "S3C"
diff --git a/drivers/mci/stm378x.c b/drivers/mci/stm378x.c
index 420c2ea92b..94b0f4ad99 100644
--- a/drivers/mci/stm378x.c
+++ b/drivers/mci/stm378x.c
@@ -52,7 +52,7 @@
# define SSP_CTRL0_SFTRST (1 << 31)
# define SSP_CTRL0_CLKGATE (1 << 30)
# define SSP_CTRL0_RUN (1 << 29)
-# define SSP_CTRL0_LOCK_CS (1 << 29)
+# define SSP_CTRL0_LOCK_CS (1 << 27)
# define SSP_CTRL0_READ (1 << 25)
# define SSP_CTRL0_IGNORE_CRC (1 << 26)
# define SSP_CTRL0_DATA_XFER (1 << 24)
@@ -61,37 +61,77 @@
# define SSP_CTRL0_LONG_RESP (1 << 19)
# define SSP_CTRL0_GET_RESP (1 << 17)
# define SSP_CTRL0_ENABLE (1 << 16)
+#ifdef CONFIG_ARCH_IMX23
# define SSP_CTRL0_XFER_COUNT(x) ((x) & 0xffff)
+#endif
#define HW_SSP_CMD0 0x010
# define SSP_CMD0_SLOW_CLK (1 << 22)
# define SSP_CMD0_CONT_CLK (1 << 21)
# define SSP_CMD0_APPEND_8CYC (1 << 20)
+#ifdef CONFIG_ARCH_IMX23
# define SSP_CMD0_BLOCK_SIZE(x) (((x) & 0xf) << 16)
# define SSP_CMD0_BLOCK_COUNT(x) (((x) & 0xff) << 8)
+#endif
# define SSP_CMD0_CMD(x) ((x) & 0xff)
#define HW_SSP_CMD1 0x020
-#define HW_SSP_COMPREF 0x030
-#define HW_SSP_COMPMASK 0x040
-#define HW_SSP_TIMING 0x050
+
+#ifdef CONFIG_ARCH_IMX23
+# define HW_SSP_COMPREF 0x030
+# define HW_SSP_COMPMASK 0x040
+# define HW_SSP_TIMING 0x050
+# define HW_SSP_CTRL1 0x060
+# define HW_SSP_DATA 0x070
+#endif
+#ifdef CONFIG_ARCH_IMX28
+# define HW_SSP_XFER_COUNT 0x30
+# define HW_SSP_BLOCK_SIZE 0x40
+# define SSP_BLOCK_SIZE(x) ((x) & 0xf)
+# define SSP_BLOCK_COUNT(x) (((x) & 0xffffff) << 4)
+# define HW_SSP_COMPREF 0x050
+# define HW_SSP_COMPMASK 0x060
+# define HW_SSP_TIMING 0x070
+# define HW_SSP_CTRL1 0x080
+# define HW_SSP_DATA 0x090
+#endif
+/* bit definition for register HW_SSP_TIMING */
# define SSP_TIMING_TIMEOUT_MASK (0xffff0000)
# define SSP_TIMING_TIMEOUT(x) ((x) << 16)
# define SSP_TIMING_CLOCK_DIVIDE(x) (((x) & 0xff) << 8)
# define SSP_TIMING_CLOCK_RATE(x) ((x) & 0xff)
-#define HW_SSP_CTRL1 0x060
+/* bit definition for register HW_SSP_CTRL1 */
# define SSP_CTRL1_POLARITY (1 << 9)
# define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4)
# define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf)
-#define HW_SSP_DATA 0x070
-#define HW_SSP_SDRESP0 0x080
-#define HW_SSP_SDRESP1 0x090
-#define HW_SSP_SDRESP2 0x0A0
-#define HW_SSP_SDRESP3 0x0B0
+#ifdef CONFIG_ARCH_IMX23
+# define HW_SSP_SDRESP0 0x080
+# define HW_SSP_SDRESP1 0x090
+# define HW_SSP_SDRESP2 0x0A0
+# define HW_SSP_SDRESP3 0x0B0
+#endif
+#ifdef CONFIG_ARCH_IMX28
+# define HW_SSP_SDRESP0 0x0A0
+# define HW_SSP_SDRESP1 0x0B0
+# define HW_SSP_SDRESP2 0x0C0
+# define HW_SSP_SDRESP3 0x0D0
+#endif
+
+#ifdef CONFIG_ARCH_IMX28
+# define HW_SSP_DDR_CTRL 0x0E0
+# define HW_SSP_DLL_CTRL 0x0F0
+#endif
+
+#ifdef CONFIG_ARCH_IMX23
+# define HW_SSP_STATUS 0x0C0
+#endif
+#ifdef CONFIG_ARCH_IMX28
+# define HW_SSP_STATUS 0x100
+#endif
-#define HW_SSP_STATUS 0x0C0
+/* bit definition for register HW_SSP_STATUS */
# define SSP_STATUS_PRESENT (1 << 31)
# define SSP_STATUS_SD_PRESENT (1 << 29)
# define SSP_STATUS_CARD_DETECT (1 << 28)
@@ -111,11 +151,23 @@
SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR | \
SSP_STATUS_RESP_TIMEOUT | SSP_STATUS_DATA_CRC_ERR | SSP_STATUS_TIMEOUT)
-#define HW_SSP_DEBUG 0x100
-#define HW_SSP_VERSION 0x110
+#ifdef CONFIG_ARCH_IMX28
+# define HW_SSP_DLL_STS 0x110
+#endif
+
+#ifdef CONFIG_ARCH_IMX23
+# define HW_SSP_DEBUG 0x100
+# define HW_SSP_VERSION 0x110
+#endif
+
+#ifdef CONFIG_ARCH_IMX28
+# define HW_SSP_DEBUG 0x120
+# define HW_SSP_VERSION 0x130
+#endif
struct stm_mci_host {
unsigned clock; /* current clock speed in Hz ("0" if disabled) */
+ unsigned index;
#ifdef CONFIG_MCI_INFO
unsigned f_min;
unsigned f_max;
@@ -124,6 +176,18 @@ struct stm_mci_host {
};
/**
+ * Get the SSP clock rate
+ * @param hw_dev Host interface device instance
+ * @return Unit's clock in [Hz]
+ */
+static unsigned get_unit_clock(struct device_d *hw_dev)
+{
+ struct stm_mci_host *host_data = GET_HOST_DATA(hw_dev);
+
+ return imx_get_sspclk(host_data->index);
+}
+
+/**
* Get MCI cards response if defined for the type of command
* @param hw_dev Host interface device instance
* @param cmd Command description
@@ -417,6 +481,7 @@ static int stm_mci_adtc(struct device_d *hw_dev, struct mci_cmd *cmd,
xfer_cnt = log2blocksize = block_cnt = 0;
/* setup command and transfer parameters */
+#ifdef CONFIG_ARCH_IMX23
writel(prepare_transfer_setup(cmd->resp_type, data != NULL ? data->flags : 0) |
SSP_CTRL0_BUS_WIDTH(host_data->bus_width) |
(xfer_cnt != 0 ? SSP_CTRL0_DATA_XFER : 0) | /* command plus data */
@@ -430,6 +495,23 @@ static int stm_mci_adtc(struct device_d *hw_dev, struct mci_cmd *cmd,
SSP_CMD0_BLOCK_COUNT(block_cnt) |
(cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION ? SSP_CMD0_APPEND_8CYC : 0),
hw_dev->map_base + HW_SSP_CMD0);
+#endif
+#ifdef CONFIG_ARCH_IMX28
+ writel(prepare_transfer_setup(cmd->resp_type, data != NULL ? data->flags : 0) |
+ SSP_CTRL0_BUS_WIDTH(host_data->bus_width) |
+ (xfer_cnt != 0 ? SSP_CTRL0_DATA_XFER : 0) | /* command plus data */
+ SSP_CTRL0_ENABLE,
+ hw_dev->map_base + HW_SSP_CTRL0);
+ writel(xfer_cnt, hw_dev->map_base + HW_SSP_XFER_COUNT);
+
+ /* prepare the command and the transfered data count */
+ writel(SSP_CMD0_CMD(cmd->cmdidx) |
+ (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION ? SSP_CMD0_APPEND_8CYC : 0),
+ hw_dev->map_base + HW_SSP_CMD0);
+ writel(SSP_BLOCK_SIZE(log2blocksize) |
+ SSP_BLOCK_COUNT(block_cnt),
+ hw_dev->map_base + HW_SSP_BLOCK_SIZE);
+#endif
/* prepare command's arguments */
writel(cmd->cmdarg, hw_dev->map_base + HW_SSP_CMD1);
@@ -481,10 +563,10 @@ static unsigned setup_clock_speed(struct device_d *hw_dev, unsigned nc)
return 0;
}
- ssp = imx_get_sspclk(0) * 1000;
+ ssp = get_unit_clock(hw_dev);
for (div = 2; div < 255; div += 2) {
- rate = (((ssp + (nc >> 1) ) / nc) + (div >> 1)) / div;
+ rate = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ssp, nc), div);
if (rate <= 0x100)
break;
}
@@ -657,21 +739,44 @@ static int stm_mci_probe(struct device_d *hw_dev)
host->voltages = pd->voltages;
host->host_caps = pd->caps;
+#ifdef CONFIG_ARCH_IMX23
+ host_data->index = 0; /* there is only one clock for all */
+#endif
+#ifdef CONFIG_ARCH_IMX28
+ /* one dedicated clock per unit */
+ switch (hw_dev->map_base) {
+ case IMX_SSP0_BASE:
+ host_data->index = 0;
+ break;
+ case IMX_SSP1_BASE:
+ host_data->index = 1;
+ break;
+ case IMX_SSP2_BASE:
+ host_data->index = 2;
+ break;
+ case IMX_SSP3_BASE:
+ host_data->index = 3;
+ break;
+ default:
+ pr_debug("Unknown SSP unit at address 0x%08x\n", hw_dev->map_base);
+ return 0;
+ }
+#endif
if (pd->f_min == 0) {
- host->f_min = imx_get_sspclk(0) / 254U / 256U * 1000U;
+ host->f_min = get_unit_clock(hw_dev) / 254 / 256;
pr_debug("Min. frequency is %u Hz\n", host->f_min);
} else {
host->f_min = pd->f_min;
pr_debug("Min. frequency is %u Hz, could be %u Hz\n",
- host->f_min, imx_get_sspclk(0) / 254U / 256U * 1000U);
+ host->f_min, get_unit_clock(hw_dev) / 254 / 256);
}
if (pd->f_max == 0) {
- host->f_max = imx_get_sspclk(0) / 2U / 1U * 1000U;
+ host->f_max = get_unit_clock(hw_dev) / 2 / 1;
pr_debug("Max. frequency is %u Hz\n", host->f_max);
} else {
host->f_max = pd->f_max;
pr_debug("Max. frequency is %u Hz, could be %u Hz\n",
- host->f_max, imx_get_sspclk(0) / 2U / 1U * 1000U);
+ host->f_max, get_unit_clock(hw_dev) / 2 / 1);
}
#ifdef CONFIG_MCI_INFO
diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c
index 9c8de77dab..e63e42eb1c 100644
--- a/drivers/net/fec_imx.c
+++ b/drivers/net/fec_imx.c
@@ -29,10 +29,13 @@
#include <asm/mmu.h>
#include <asm/io.h>
+#include <mach/generic.h>
#include <mach/imx-regs.h>
#include <clock.h>
#include <mach/clock.h>
-#include <mach/iim.h>
+#ifndef CONFIG_ARCH_STM
+# include <mach/iim.h>
+#endif
#include <xfuncs.h>
#include "fec_imx.h"
@@ -151,6 +154,39 @@ static int fec_tx_task_disable(struct fec_priv *fec)
}
/**
+ * Swap endianess to send data on an i.MX28 based platform
+ * @param buf Pointer to little endian data
+ * @param len Size in words (max. 1500 bytes)
+ * @return Pointer to the big endian data
+ */
+static void *imx28_fix_endianess_wr(uint32_t *buf, unsigned wlen)
+{
+ unsigned u;
+ static uint32_t data[376]; /* = 1500 bytes + 4 bytes */
+
+ for (u = 0; u < wlen; u++, buf++)
+ data[u] = __swab32(*buf);
+
+ return data;
+}
+
+/**
+ * Swap endianess to read data on an i.MX28 based platform
+ * @param buf Pointer to little endian data
+ * @param len Size in words (max. 1500 bytes)
+ *
+ * TODO: Check for the risk of destroying some other data behind the buffer
+ * if its size is not a multiple of 4.
+ */
+static void imx28_fix_endianess_rd(uint32_t *buf, unsigned wlen)
+{
+ unsigned u;
+
+ for (u = 0; u < wlen; u++, buf++)
+ *buf = __swab32(*buf);
+}
+
+/**
* Initialize receive task's buffer descriptors
* @param[in] fec all we know about the device yet
* @param[in] count receive buffer count to be allocated
@@ -233,7 +269,11 @@ static void fec_rbd_clean(int last, struct buffer_descriptor __iomem *pRbd)
static int fec_get_hwaddr(struct eth_device *dev, unsigned char *mac)
{
+#ifdef CONFIG_ARCH_STM
+ return -1;
+#else
return imx_iim_get_mac(mac);
+#endif
}
static int fec_set_hwaddr(struct eth_device *dev, unsigned char *mac)
@@ -270,12 +310,13 @@ static int fec_init(struct eth_device *dev)
/*
* Frame length=1518; 7-wire mode
*/
- writel((1518 << 16), fec->regs + FEC_R_CNTRL);
+ writel(FEC_R_CNTRL_MAX_FL(1518), fec->regs + FEC_R_CNTRL);
} else {
/*
* Frame length=1518; MII mode;
*/
- writel((1518 << 16) | (1 << 2), fec->regs + FEC_R_CNTRL);
+ writel(FEC_R_CNTRL_MAX_FL(1518) | FEC_R_CNTRL_MII_MODE,
+ fec->regs + FEC_R_CNTRL);
/*
* Set MII_SPEED = (1/(mii_speed * 2)) * System Clock
* and do not drop the Preamble.
@@ -285,16 +326,26 @@ static int fec_init(struct eth_device *dev)
}
if (fec->xcv_type == RMII) {
- /* disable the gasket and wait */
- writel(0, fec->regs + FEC_MIIGSK_ENR);
- while (readl(fec->regs + FEC_MIIGSK_ENR) & FEC_MIIGSK_ENR_READY)
- udelay(1);
+ if (cpu_is_mx28()) {
+ /* just another way to enable RMII */
+ uint32_t reg = readl(fec->regs + FEC_R_CNTRL);
+ writel(reg | FEC_R_CNTRL_RMII_MODE
+ /* the linux driver add these bits, why not we? */
+ /* | FEC_R_CNTRL_FCE | */
+ /* FEC_R_CNTRL_NO_LGTH_CHECK */,
+ fec->regs + FEC_R_CNTRL);
+ } else {
+ /* disable the gasket and wait */
+ writel(0, fec->regs + FEC_MIIGSK_ENR);
+ while (readl(fec->regs + FEC_MIIGSK_ENR) & FEC_MIIGSK_ENR_READY)
+ udelay(1);
- /* configure the gasket for RMII, 50 MHz, no loopback, no echo */
- writel(FEC_MIIGSK_CFGR_IF_MODE_RMII, fec->regs + FEC_MIIGSK_CFGR);
+ /* configure the gasket for RMII, 50 MHz, no loopback, no echo */
+ writel(FEC_MIIGSK_CFGR_IF_MODE_RMII, fec->regs + FEC_MIIGSK_CFGR);
- /* re-enable the gasket */
- writel(FEC_MIIGSK_ENR_EN, fec->regs + FEC_MIIGSK_ENR);
+ /* re-enable the gasket */
+ writel(FEC_MIIGSK_ENR_EN, fec->regs + FEC_MIIGSK_ENR);
+ }
}
/*
@@ -419,6 +470,9 @@ static int fec_send(struct eth_device *dev, void *eth_data, int data_length)
* Note: We are always using the first buffer for transmission,
* the second will be empty and only used to stop the DMA engine
*/
+ if (cpu_is_mx28())
+ eth_data = imx28_fix_endianess_wr(eth_data, (data_length + 3) >> 2);
+
writew(data_length, &fec->tbd_base[fec->tbd_index].data_length);
writel((uint32_t)(eth_data), &fec->tbd_base[fec->tbd_index].data_pointer);
@@ -476,18 +530,19 @@ static int fec_recv(struct eth_device *dev)
ievent = readl(fec->regs + FEC_IEVENT);
writel(ievent, fec->regs + FEC_IEVENT);
- if (ievent & (FEC_IEVENT_BABT | FEC_IEVENT_XFIFO_ERROR |
- FEC_IEVENT_RFIFO_ERROR)) {
+ if (ievent & FEC_IEVENT_BABT) {
/* BABT, Rx/Tx FIFO errors */
fec_halt(dev);
fec_init(dev);
printf("some error: 0x%08x\n", ievent);
return 0;
}
- if (ievent & FEC_IEVENT_HBERR) {
- /* Heartbeat error */
- writel(readl(fec->regs + FEC_X_CNTRL) | 0x1,
- fec->regs + FEC_X_CNTRL);
+ if (!cpu_is_mx28()) {
+ if (ievent & FEC_IEVENT_HBERR) {
+ /* Heartbeat error */
+ writel(readl(fec->regs + FEC_X_CNTRL) | 0x1,
+ fec->regs + FEC_X_CNTRL);
+ }
}
if (ievent & FEC_IEVENT_GRA) {
/* Graceful stop complete */
@@ -507,6 +562,12 @@ static int fec_recv(struct eth_device *dev)
if (!(bd_status & FEC_RBD_EMPTY)) {
if ((bd_status & FEC_RBD_LAST) && !(bd_status & FEC_RBD_ERR) &&
((readw(&rbd->data_length) - 4) > 14)) {
+
+ if (cpu_is_mx28())
+ imx28_fix_endianess_rd(
+ (void *)readl(&rbd->data_pointer),
+ (readw(&rbd->data_length) + 3) >> 2);
+
/*
* Get buffer address and size
*/
diff --git a/drivers/net/fec_imx.h b/drivers/net/fec_imx.h
index ce0fd89ec4..e07071a883 100644
--- a/drivers/net/fec_imx.h
+++ b/drivers/net/fec_imx.h
@@ -63,8 +63,19 @@
#define FEC_MIIGSK_ENR_READY (1 << 2)
#define FEC_MIIGSK_ENR_EN (1 << 1)
-
-#define FEC_IEVENT_HBERR 0x80000000
+#define FEC_R_CNTRL_GRS (1 << 31)
+#define FEC_R_CNTRL_NO_LGTH_CHECK (1 << 30)
+#ifdef CONFIG_ARCH_IMX28
+# define FEC_R_CNTRL_MAX_FL(x) (((x) & 0x3fff) << 16)
+#else
+# define FEC_R_CNTRL_MAX_FL(x) (((x) & 0x7ff) << 16)
+#endif
+#define FEC_R_CNTRL_RMII_10T (1 << 9) /* i.MX28 specific */
+#define FEC_R_CNTRL_RMII_MODE (1 << 8) /* i.MX28 specific */
+#define FEC_R_CNTRL_FCE (1 << 5)
+#define FEC_R_CNTRL_MII_MODE (1 << 2)
+
+#define FEC_IEVENT_HBERR 0x80000000 /* Note: Not on i.MX28 */
#define FEC_IEVENT_BABR 0x40000000
#define FEC_IEVENT_BABT 0x20000000
#define FEC_IEVENT_GRA 0x10000000
@@ -73,10 +84,8 @@
#define FEC_IEVENT_LATE_COL 0x00200000
#define FEC_IEVENT_COL_RETRY_LIM 0x00100000
#define FEC_IEVENT_XFIFO_UN 0x00080000
-#define FEC_IEVENT_XFIFO_ERROR 0x00040000
-#define FEC_IEVENT_RFIFO_ERROR 0x00020000
-#define FEC_IMASK_HBERR 0x80000000
+#define FEC_IMASK_HBERR 0x80000000 /* Note: Not on i.MX28 */
#define FEC_IMASK_BABR 0x40000000
#define FEC_IMASK_BABT 0x20000000
#define FEC_IMASK_GRA 0x10000000
@@ -84,8 +93,6 @@
#define FEC_IMASK_LATE_COL 0x00200000
#define FEC_IMASK_COL_RETRY_LIM 0x00100000
#define FEC_IMASK_XFIFO_UN 0x00080000
-#define FEC_IMASK_XFIFO_ERROR 0x00040000
-#define FEC_IMASK_RFIFO_ERROR 0x00020000
#define FEC_RCNTRL_MAX_FL_SHIFT 16
#define FEC_RCNTRL_LOOP 0x01
@@ -124,6 +131,8 @@
* @brief Receive & Transmit Buffer Descriptor definitions
*
* Note: The first BD must be aligned (see DB_ALIGNMENT)
+ *
+ * BTW: Don't trust the i.MX27 and i.MX28 data sheet
*/
struct buffer_descriptor {
uint16_t data_length; /**< payload's length in bytes */
diff --git a/drivers/net/netx_eth.c b/drivers/net/netx_eth.c
index 7d55a61384..fd40f6271a 100644
--- a/drivers/net/netx_eth.c
+++ b/drivers/net/netx_eth.c
@@ -7,7 +7,6 @@
#include <mach/netx-eth.h>
#include <mach/netx-regs.h>
#include <xfuncs.h>
-#include <miidev.h>
#include <init.h>
#include <driver.h>
diff --git a/drivers/nor/cfi_flash.c b/drivers/nor/cfi_flash.c
index 2fa2c6b900..efe87856c4 100644
--- a/drivers/nor/cfi_flash.c
+++ b/drivers/nor/cfi_flash.c
@@ -77,59 +77,40 @@ static uint flash_offset_cfi[2]={FLASH_OFFSET_CFI,FLASH_OFFSET_CFI_ALT};
static void flash_add_byte (struct flash_info *info, cfiword_t * cword, uchar c)
{
-#if defined(__LITTLE_ENDIAN)
- unsigned short w;
- unsigned int l;
- unsigned long long ll;
-#endif
-
if (bankwidth_is_1(info)) {
- cword->c = c;
- } else if (bankwidth_is_2(info)) {
-#if defined(__LITTLE_ENDIAN)
- w = c;
- w <<= 8;
- cword->w = (cword->w >> 8) | w;
-#else
- cword->w = (cword->w << 8) | c;
-#endif
- } else if (bankwidth_is_4(info)) {
-#if defined(__LITTLE_ENDIAN)
- l = c;
- l <<= 24;
- cword->l = (cword->l >> 8) | l;
-#else
- cword->l = (cword->l << 8) | c;
-#endif
- } else if (bankwidth_is_8(info)) {
-#if defined(__LITTLE_ENDIAN)
- ll = c;
- ll <<= 56;
- cword->ll = (cword->ll >> 8) | ll;
+ *cword = c;
+ return;
+ }
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ *cword = (*cword << 8) | c;
#else
- cword->ll = (cword->ll << 8) | c;
+
+ if (bankwidth_is_2(info))
+ *cword = (*cword >> 8) | (u16)c << 8;
+ else if (bankwidth_is_4(info))
+ *cword = (*cword >> 8) | (u32)c << 24;
+ else if (bankwidth_is_8(info))
+ *cword = (*cword >> 8) | (u64)c << 56;
#endif
- }
}
static int flash_write_cfiword (struct flash_info *info, ulong dest,
cfiword_t cword)
{
- void *dstaddr;
+ void *dstaddr = (void *)dest;
int flag;
- dstaddr = (uchar *) dest;
-
/* Check if Flash is (sufficiently) erased */
- if (bankwidth_is_1(info)) {
- flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
- } else if (bankwidth_is_2(info)) {
- flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
- } else if (bankwidth_is_4(info)) {
- flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
- } else if (bankwidth_is_8(info)) {
- flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll);
- } else
+ if (bankwidth_is_1(info))
+ flag = ((flash_read8(dstaddr) & cword) == cword);
+ else if (bankwidth_is_2(info))
+ flag = ((flash_read16(dstaddr) & cword) == cword);
+ else if (bankwidth_is_4(info))
+ flag = ((flash_read32(dstaddr) & cword) == cword);
+ else if (bankwidth_is_8(info))
+ flag = ((flash_read64(dstaddr) & cword) == cword);
+ else
return 2;
if (!flag)
@@ -185,10 +166,8 @@ static void flash_printqry (struct cfi_qry *qry)
*/
uchar flash_read_uchar (struct flash_info *info, uint offset)
{
- uchar *cp;
-
- cp = flash_make_addr (info, 0, offset);
-#if defined(__LITTLE_ENDIAN)
+ uchar *cp = flash_make_addr(info, 0, offset);
+#if __BYTE_ORDER == __LITTLE_ENDIAN
return flash_read8(cp);
#else
return flash_read8(cp + info->portwidth - 1);
@@ -216,7 +195,7 @@ static ulong flash_read_long (struct flash_info *info, flash_sect_t sect, uint o
debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x));
}
#endif
-#if defined(__LITTLE_ENDIAN)
+#if __BYTE_ORDER == __LITTLE_ENDIAN
retval = ((flash_read8(addr) << 16) |
(flash_read8(addr + info->portwidth) << 24) |
(flash_read8(addr + 2 * info->portwidth)) |
@@ -485,7 +464,7 @@ static int __cfi_erase(struct cdev *cdev, size_t count, unsigned long offset,
unsigned long start, end;
int i, ret = 0;
- debug("%s: erase 0x%08x (size %d)\n", __func__, offset, count);
+ debug("%s: erase 0x%08lx (size %d)\n", __func__, offset, count);
start = find_sector(finfo, cdev->dev->map_base + offset);
end = find_sector(finfo, cdev->dev->map_base + offset + count - 1);
@@ -498,6 +477,11 @@ static int __cfi_erase(struct cdev *cdev, size_t count, unsigned long offset,
if (ret)
goto out;
+ if (ctrlc()) {
+ ret = -EINTR;
+ goto out;
+ }
+
if (verbose)
show_progress(i - start);
}
@@ -534,7 +518,7 @@ static int write_buff (struct flash_info *info, const uchar * src, ulong addr, u
/* handle unaligned start */
if ((aln = addr - wp) != 0) {
- cword.l = 0;
+ cword = 0;
p = (uchar*)wp;
for (i = 0; i < aln; ++i)
flash_add_byte (info, &cword, flash_read8(p + i));
@@ -560,7 +544,7 @@ static int write_buff (struct flash_info *info, const uchar * src, ulong addr, u
while (cnt >= info->portwidth) {
/* prohibit buffer write when buffer_size is 1 */
if (info->buffer_size == 1) {
- cword.l = 0;
+ cword = 0;
for (i = 0; i < info->portwidth; i++)
flash_add_byte (info, &cword, *src++);
if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
@@ -583,7 +567,7 @@ static int write_buff (struct flash_info *info, const uchar * src, ulong addr, u
}
#else
while (cnt >= info->portwidth) {
- cword.l = 0;
+ cword = 0;
for (i = 0; i < info->portwidth; i++) {
flash_add_byte (info, &cword, *src++);
}
@@ -600,7 +584,7 @@ static int write_buff (struct flash_info *info, const uchar * src, ulong addr, u
/*
* handle unaligned tail bytes
*/
- cword.l = 0;
+ cword = 0;
p = (uchar*)wp;
for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) {
flash_add_byte (info, &cword, *src++);
@@ -651,8 +635,8 @@ static int cfi_protect(struct cdev *cdev, size_t count, unsigned long offset, in
int i, ret = 0;
const char *action = (prot? "protect" : "unprotect");
- printf("%s: %s 0x%08x (size %d)\n", __FUNCTION__,
- action, cdev->dev->map_base + offset, count);
+ printf("%s: %s 0x%08lx (size %d)\n", __FUNCTION__,
+ action, cdev->dev->map_base + offset, count);
start = find_sector(finfo, cdev->dev->map_base + offset);
end = find_sector(finfo, cdev->dev->map_base + offset + count - 1);
@@ -672,7 +656,7 @@ static ssize_t cfi_write(struct cdev *cdev, const void *buf, size_t count, unsig
struct flash_info *finfo = (struct flash_info *)cdev->priv;
int ret;
- debug("cfi_write: buf=0x%08x addr=0x%08x count=0x%08x\n",buf, cdev->dev->map_base + offset, count);
+ debug("cfi_write: buf=0x%p addr=0x%08lx count=0x%08x\n",buf, cdev->dev->map_base + offset, count);
ret = write_buff (finfo, buf, cdev->dev->map_base + offset, count);
return ret == 0 ? count : -1;
@@ -836,17 +820,14 @@ int flash_generic_status_check (struct flash_info *info, flash_sect_t sector,
/*
* make a proper sized command based on the port and chip widths
*/
-void flash_make_cmd (struct flash_info *info, uchar cmd, void *cmdbuf)
+void flash_make_cmd(struct flash_info *info, u8 cmd, cfiword_t *cmdbuf)
{
- int i;
- uchar *cp = (uchar *) cmdbuf;
+ cfiword_t result = 0;
+ int i = info->portwidth / info->chipwidth;
-#if defined(__LITTLE_ENDIAN)
- for (i = info->portwidth; i > 0; i--)
-#else
- for (i = 1; i <= info->portwidth; i++)
-#endif
- *cp++ = (i & (info->chipwidth - 1)) ? '\0' : cmd;
+ while (i--)
+ result = (result << (8 * info->chipwidth)) | cmd;
+ *cmdbuf = result;
}
/*
@@ -860,6 +841,7 @@ void flash_write_cmd (struct flash_info *info, flash_sect_t sect, uint offset, u
addr = flash_make_addr (info, sect, offset);
flash_make_cmd (info, cmd, &cword);
+ debug("%s: %p %lX %X => %p %llX\n", __FUNCTION__, info, sect, offset, addr, cword);
flash_write_word(info, cword, addr);
}
@@ -874,14 +856,14 @@ int flash_isequal (struct flash_info *info, flash_sect_t sect, uint offset, ucha
debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr);
if (bankwidth_is_1(info)) {
- debug ("is= %x %x\n", flash_read8(addr), cword.c);
- retval = (flash_read8(addr) == cword.c);
+ debug ("is= %x %x\n", flash_read8(addr), (u8)cword);
+ retval = (flash_read8(addr) == cword);
} else if (bankwidth_is_2(info)) {
- debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w);
- retval = (flash_read16(addr) == cword.w);
+ debug ("is= %4.4x %4.4x\n", flash_read16(addr), (u16)cword);
+ retval = (flash_read16(addr) == cword);
} else if (bankwidth_is_4(info)) {
- debug ("is= %8.8lx %8.8lx\n", flash_read32(addr), cword.l);
- retval = (flash_read32(addr) == cword.l);
+ debug ("is= %8.8lx %8.8lx\n", flash_read32(addr), (u32)cword);
+ retval = (flash_read32(addr) == cword);
} else if (bankwidth_is_8(info)) {
#ifdef DEBUG
{
@@ -889,11 +871,11 @@ int flash_isequal (struct flash_info *info, flash_sect_t sect, uint offset, ucha
char str2[20];
print_longlong (str1, flash_read32(addr));
- print_longlong (str2, cword.ll);
+ print_longlong (str2, cword);
debug ("is= %s %s\n", str1, str2);
}
#endif
- retval = (flash_read32(addr) == cword.ll);
+ retval = (flash_read64(addr) == cword);
} else
retval = 0;
@@ -902,20 +884,19 @@ int flash_isequal (struct flash_info *info, flash_sect_t sect, uint offset, ucha
int flash_isset (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd)
{
- void *addr;
+ void *addr = flash_make_addr (info, sect, offset);
cfiword_t cword;
int retval;
- addr = flash_make_addr (info, sect, offset);
flash_make_cmd (info, cmd, &cword);
if (bankwidth_is_1(info)) {
- retval = ((flash_read8(addr) & cword.c) == cword.c);
+ retval = ((flash_read8(addr) & cword) == cword);
} else if (bankwidth_is_2(info)) {
- retval = ((flash_read16(addr) & cword.w) == cword.w);
+ retval = ((flash_read16(addr) & cword) == cword);
} else if (bankwidth_is_4(info)) {
- retval = ((flash_read32(addr) & cword.l) == cword.l);
+ retval = ((flash_read32(addr) & cword) == cword);
} else if (bankwidth_is_8(info)) {
- retval = ((flash_read64(addr) & cword.ll) == cword.ll);
+ retval = ((flash_read64(addr) & cword) == cword);
} else
retval = 0;
@@ -995,7 +976,6 @@ static void cfi_init_mtd(struct flash_info *info)
static int cfi_probe (struct device_d *dev)
{
- unsigned long size = 0;
struct flash_info *info = xzalloc(sizeof(*info));
dev->priv = (void *)info;
@@ -1004,11 +984,12 @@ static int cfi_probe (struct device_d *dev)
/* Init: no FLASHes known */
info->flash_id = FLASH_UNKNOWN;
- size += info->size = flash_get_size(info, dev->map_base);
+ info->cmd_reset = FLASH_CMD_RESET;
+ info->size = flash_get_size(info, dev->map_base);
info->base = (void __iomem *)dev->map_base;
if (dev->size == 0) {
- printf("cfi_probe: size : 0x%08x\n", info->size);
+ printf("cfi_probe: size : 0x%08lx\n", info->size);
dev->size = info->size;
}
diff --git a/drivers/nor/cfi_flash.h b/drivers/nor/cfi_flash.h
index f9023dcafa..90980211e7 100644
--- a/drivers/nor/cfi_flash.h
+++ b/drivers/nor/cfi_flash.h
@@ -25,10 +25,12 @@
*/
#include <driver.h>
+#include <asm/byteorder.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
typedef unsigned long flash_sect_t;
+typedef u64 cfiword_t;
struct cfi_cmd_set;
/*-----------------------------------------------------------------------
@@ -238,7 +240,7 @@ int flash_generic_status_check (struct flash_info *info, flash_sect_t sector,
uint64_t tout, char *prompt);
int flash_isequal (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd);
-void flash_make_cmd (struct flash_info *info, uchar cmd, void *cmdbuf);
+void flash_make_cmd(struct flash_info *info, uchar cmd, cfiword_t *cmdbuf);
static inline void flash_write8(u8 value, void *addr)
{
@@ -316,26 +318,19 @@ u32 jedec_read_mfr(struct flash_info *info);
#define bankwidth_is_8(info) 0
#endif
-typedef union {
- unsigned char c;
- unsigned short w;
- unsigned long l;
- unsigned long long ll;
-} cfiword_t;
-
static inline void flash_write_word(struct flash_info *info, cfiword_t datum, void *addr)
{
if (bankwidth_is_1(info)) {
- debug("fw addr %p val %02x\n", addr, datum.c);
- flash_write8(datum.c, addr);
+ debug("fw addr %p val %02x\n", addr, (u8)datum);
+ flash_write8(datum, addr);
} else if (bankwidth_is_2(info)) {
- debug("fw addr %p val %04x\n", addr, datum.w);
- flash_write16(datum.w, addr);
+ debug("fw addr %p val %04x\n", addr, (u16)datum);
+ flash_write16(datum, addr);
} else if (bankwidth_is_4(info)) {
- debug("fw addr %p val %08x\n", addr, datum.l);
- flash_write32(datum.l, addr);
+ debug("fw addr %p val %08x\n", addr, (u32)datum);
+ flash_write32(datum, addr);
} else if (bankwidth_is_8(info)) {
- flash_write64(datum.ll, addr);
+ flash_write64(datum, addr);
}
}
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 3a8882fd1d..04d9b98174 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -21,7 +21,7 @@ config DRIVER_SERIAL_IMX
config DRIVER_SERIAL_STM378X
depends on ARCH_STM
default y
- bool "i.MX23 serial driver"
+ bool "i.MX23/i.MX28 serial driver"
config DRIVER_SERIAL_NETX
depends on ARCH_NETX
diff --git a/drivers/serial/stm-serial.c b/drivers/serial/stm-serial.c
index 90563f599e..a225e30cdb 100644
--- a/drivers/serial/stm-serial.c
+++ b/drivers/serial/stm-serial.c
@@ -108,7 +108,7 @@ static int stm_serial_setbaudrate(struct console_device *cdev, int new_baudrate)
writel(0, dev->map_base + UARTDBGCR);
/* Calculate and set baudrate */
- quot = (imx_get_xclk() * 4000) / new_baudrate;
+ quot = (imx_get_xclk() * 4) / new_baudrate;
writel(quot & 0x3f, dev->map_base + UARTDBGFBRD);
writel(quot >> 6, dev->map_base + UARTDBGIBRD);
@@ -134,13 +134,6 @@ static int stm_clocksource_clock_change(struct notifier_block *nb, unsigned long
static int stm_serial_init_port(struct console_device *cdev)
{
struct device_d *dev = cdev->dev;
- /*
- * If the board specific file registers this console we should force
- * the usage of the debug UART pins, to be able to let the user see
- * the output, even if the board file forgets to configure these pins.
- */
- imx_gpio_mode(PWM1_DUART_TX);
- imx_gpio_mode(PWM0_DUART_RX);
/* Disable UART */
writel(0, dev->map_base + UARTDBGCR);
diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c
index 2ad1bfa499..1857d6c672 100644
--- a/drivers/spi/imx_spi.c
+++ b/drivers/spi/imx_spi.c
@@ -224,9 +224,6 @@ static unsigned int cspi_2_3_xchg_single(struct imx_spi *imx, unsigned int data)
return readl(base + CSPI_2_3_RXDATA);
}
-/* FIXME: include/linux/kernel.h */
-#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
-
static unsigned int cspi_2_3_clkdiv(unsigned int fin, unsigned int fspi)
{
/*
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 7a89a3f206..c69308e749 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -20,4 +20,11 @@ config DRIVER_VIDEO_IMX_IPU
Add support for the IPU framebuffer device found on
i.MX31 and i.MX35 CPUs.
+config DRIVER_VIDEO_STM
+ bool "i.MX23/28 framebuffer driver"
+ depends on ARCH_STM
+ help
+ Say 'Y' here to enable framebuffer and splash screen support for
+ i.MX23 and i.MX28 based systems.
+
endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 179f0c42f6..a217a0b92b 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_VIDEO) += fb.o
+obj-$(CONFIG_DRIVER_VIDEO_STM) += stm.o
obj-$(CONFIG_DRIVER_VIDEO_IMX) += imx.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPU) += imx-ipu-fb.o
diff --git a/drivers/video/fb.c b/drivers/video/fb.c
index ab2c5eb2f6..aad0e1f409 100644
--- a/drivers/video/fb.c
+++ b/drivers/video/fb.c
@@ -39,14 +39,13 @@ static int fb_enable_set(struct device_d *dev, struct param_d *param,
enable = simple_strtoul(val, NULL, 0);
- if (info->enabled == !!enable)
- return 0;
-
if (enable) {
- info->fbops->fb_enable(info);
+ if (!info->enabled)
+ info->fbops->fb_enable(info);
new = "1";
} else {
- info->fbops->fb_disable(info);
+ if (info->enabled)
+ info->fbops->fb_disable(info);
new = "0";
}
@@ -57,6 +56,35 @@ static int fb_enable_set(struct device_d *dev, struct param_d *param,
return 0;
}
+static int fb_setup_mode(struct device_d *dev, struct param_d *param,
+ const char *val)
+{
+ struct fb_info *info = dev->priv;
+ int mode, ret;
+
+ if (info->enabled != 0)
+ return -EPERM;
+
+ if (!val)
+ return dev_param_set_generic(dev, param, NULL);
+
+ for (mode = 0; mode < info->num_modes; mode++) {
+ if (!strcmp(info->mode_list[mode].name, val))
+ break;
+ }
+ if (mode >= info->num_modes)
+ return -EINVAL;
+
+ info->mode = &info->mode_list[mode];
+
+ ret = info->fbops->fb_activate_var(info);
+
+ if (ret == 0)
+ dev_param_set_generic(dev, param, val);
+
+ return ret;
+}
+
static struct file_operations fb_ops = {
.read = mem_read,
.write = mem_write,
@@ -88,6 +116,12 @@ int register_framebuffer(struct fb_info *info)
dev_add_param(dev, "enable", fb_enable_set, NULL, 0);
dev_set_param(dev, "enable", "0");
+ if (info->num_modes && (info->mode_list != NULL) &&
+ (info->fbops->fb_activate_var != NULL)) {
+ dev_add_param(dev, "mode_name", fb_setup_mode, NULL, 0);
+ dev_set_param(dev, "mode_name", info->mode_list[0].name);
+ }
+
devfs_create(&info->cdev);
return 0;
diff --git a/drivers/video/stm.c b/drivers/video/stm.c
new file mode 100644
index 0000000000..f0abe4c71e
--- /dev/null
+++ b/drivers/video/stm.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2010 Juergen Beisert, Pengutronix <jbe@pengutronix.de>
+ *
+ * This is based on code from:
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <malloc.h>
+#include <errno.h>
+#include <xfuncs.h>
+#include <asm/io.h>
+#include <mach/imx-regs.h>
+#include <mach/clock.h>
+#include <mach/fb.h>
+
+#define HW_LCDIF_CTRL 0x00
+# define CTRL_SFTRST (1 << 31)
+# define CTRL_CLKGATE (1 << 30)
+# define CTRL_BYPASS_COUNT (1 << 19)
+# define CTRL_VSYNC_MODE (1 << 18)
+# define CTRL_DOTCLK_MODE (1 << 17)
+# define CTRL_DATA_SELECT (1 << 16)
+# define SET_BUS_WIDTH(x) (((x) & 0x3) << 10)
+# define SET_WORD_LENGTH(x) (((x) & 0x3) << 8)
+# define GET_WORD_LENGTH(x) (((x) >> 8) & 0x3)
+# define CTRL_MASTER (1 << 5)
+# define CTRL_DF16 (1 << 3)
+# define CTRL_DF18 (1 << 2)
+# define CTRL_DF24 (1 << 1)
+# define CTRL_RUN (1 << 0)
+
+#define HW_LCDIF_CTRL1 0x10
+# define CTRL1_FIFO_CLEAR (1 << 21)
+# define SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16)
+# define GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf)
+
+#ifdef CONFIG_ARCH_IMX28
+# define HW_LCDIF_CTRL2 0x20
+# define HW_LCDIF_TRANSFER_COUNT 0x30
+#endif
+#ifdef CONFIG_ARCH_IMX23
+# define HW_LCDIF_TRANSFER_COUNT 0x20
+#endif
+# define SET_VCOUNT(x) (((x) & 0xffff) << 16)
+# define SET_HCOUNT(x) ((x) & 0xffff)
+
+#ifdef CONFIG_ARCH_IMX28
+# define HW_LCDIF_CUR_BUF 0x40
+# define HW_LCDIF_NEXT_BUF 0x50
+#endif
+#ifdef CONFIG_ARCH_IMX23
+# define HW_LCDIF_CUR_BUF 0x30
+# define HW_LCDIF_NEXT_BUF 0x40
+#endif
+
+#define HW_LCDIF_TIMING 0x60
+# define SET_CMD_HOLD(x) (((x) & 0xff) << 24)
+# define SET_CMD_SETUP(x) (((x) & 0xff) << 16)
+# define SET_DATA_HOLD(x) (((x) & 0xff) << 8)
+# define SET_DATA_SETUP(x) ((x) & 0xff)
+
+#define HW_LCDIF_VDCTRL0 0x70
+# define VDCTRL0_ENABLE_PRESENT (1 << 28)
+# define VDCTRL0_VSYNC_POL (1 << 27) /* 0 = low active, 1 = high active */
+# define VDCTRL0_HSYNC_POL (1 << 26) /* 0 = low active, 1 = high active */
+# define VDCTRL0_DOTCLK_POL (1 << 25) /* 0 = output@falling, capturing@rising edge */
+# define VDCTRL0_ENABLE_POL (1 << 24) /* 0 = low active, 1 = high active */
+# define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21)
+# define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20)
+# define VDCTRL0_HALF_LINE (1 << 19)
+# define VDCTRL0_HALF_LINE_MODE (1 << 18)
+# define SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
+
+#define HW_LCDIF_VDCTRL1 0x80
+
+#define HW_LCDIF_VDCTRL2 0x90
+#ifdef CONFIG_ARCH_IMX28
+# define SET_HSYNC_PULSE_WIDTH(x) (((x) & 0x3fff) << 18)
+#endif
+#ifdef CONFIG_ARCH_IMX23
+# define SET_HSYNC_PULSE_WIDTH(x) (((x) & 0xff) << 24)
+#endif
+# define SET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
+
+#define HW_LCDIF_VDCTRL3 0xa0
+# define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29)
+# define VDCTRL3_VSYNC_ONLY (1 << 28)
+# define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16)
+# define SET_VERT_WAIT_CNT(x) ((x) & 0xffff)
+
+#define HW_LCDIF_VDCTRL4 0xb0
+#ifdef CONFIG_ARCH_IMX28
+# define SET_DOTCLK_DLY(x) (((x) & 0x7) << 29)
+#endif
+# define VDCTRL4_SYNC_SIGNALS_ON (1 << 18)
+# define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)
+
+#define HW_LCDIF_DVICTRL0 0xc0
+#define HW_LCDIF_DVICTRL1 0xd0
+#define HW_LCDIF_DVICTRL2 0xe0
+#define HW_LCDIF_DVICTRL3 0xf0
+#define HW_LCDIF_DVICTRL4 0x100
+
+#ifdef CONFIG_ARCH_IMX28
+# define HW_LCDIF_DATA 0x180
+#endif
+#ifdef CONFIG_ARCH_IMX23
+# define HW_LCDIF_DATA 0x1b0
+#endif
+
+#ifdef CONFIG_ARCH_IMX28
+# define HW_LCDIF_DEBUG0 0x1d0
+#endif
+#ifdef CONFIG_ARCH_IMX23
+# define HW_LCDIF_DEBUG0 0x1f0
+#endif
+# define DEBUG_HSYNC (1 < 26)
+# define DEBUG_VSYNC (1 < 25)
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define TRANSP 3
+
+struct imxfb_info {
+ void __iomem *base;
+ unsigned memory_size;
+ struct fb_info info;
+ struct device_d *hw_dev;
+ struct imx_fb_videomode *pdata;
+};
+
+/* the RGB565 true colour mode */
+static const struct fb_bitfield def_rgb565[] = {
+ [RED] = {
+ .offset = 11,
+ .length = 5,
+ },
+ [GREEN] = {
+ .offset = 5,
+ .length = 6,
+ },
+ [BLUE] = {
+ .offset = 0,
+ .length = 5,
+ },
+ [TRANSP] = { /* no support for transparency */
+ .length = 0,
+ }
+};
+
+/* the RGB666 true colour mode */
+static const struct fb_bitfield def_rgb666[] = {
+ [RED] = {
+ .offset = 16,
+ .length = 6,
+ },
+ [GREEN] = {
+ .offset = 8,
+ .length = 6,
+ },
+ [BLUE] = {
+ .offset = 0,
+ .length = 6,
+ },
+ [TRANSP] = { /* no support for transparency */
+ .length = 0,
+ }
+};
+
+/* the RGB888 true colour mode */
+static const struct fb_bitfield def_rgb888[] = {
+ [RED] = {
+ .offset = 16,
+ .length = 8,
+ },
+ [GREEN] = {
+ .offset = 8,
+ .length = 8,
+ },
+ [BLUE] = {
+ .offset = 0,
+ .length = 8,
+ },
+ [TRANSP] = { /* no support for transparency */
+ .length = 0,
+ }
+};
+
+static inline unsigned calc_line_length(unsigned ppl, unsigned bpp)
+{
+ if (bpp == 24)
+ bpp = 32;
+ return (ppl * bpp) >> 3;
+}
+
+static int stmfb_memory_mmgt(struct fb_info *fb_info, unsigned size)
+{
+ struct imxfb_info *fbi = fb_info->priv;
+
+ if (fbi->memory_size != 0) {
+ free(fb_info->screen_base);
+ fb_info->screen_base = NULL;
+ fbi->memory_size = 0;
+ }
+
+ if (fbi->memory_size == 0) {
+ fb_info->screen_base = xzalloc(size);
+ fbi->memory_size = size;
+ }
+
+ return 0;
+}
+
+static void stmfb_enable_controller(struct fb_info *fb_info)
+{
+ struct imxfb_info *fbi = fb_info->priv;
+ uint32_t reg, last_reg;
+ unsigned loop, edges;
+
+ /*
+ * Sometimes some data is still present in the FIFO. This leads into
+ * a correct but shifted picture. Clearing the FIFO helps
+ */
+ writel(CTRL1_FIFO_CLEAR, fbi->base + HW_LCDIF_CTRL1 + BIT_SET);
+
+ /* if it was disabled, re-enable the mode again */
+ reg = readl(fbi->base + HW_LCDIF_CTRL);
+ reg |= CTRL_DOTCLK_MODE;
+ writel(reg, fbi->base + HW_LCDIF_CTRL);
+
+ /* enable the SYNC signals first, then the DMA engine */
+ reg = readl(fbi->base + HW_LCDIF_VDCTRL4);
+ reg |= VDCTRL4_SYNC_SIGNALS_ON;
+ writel(reg, fbi->base + HW_LCDIF_VDCTRL4);
+
+ /*
+ * Give the attached LC display or monitor a chance to sync into
+ * our signals.
+ * Wait for at least 2 VSYNCs = four VSYNC edges
+ */
+ edges = 4;
+
+ while (edges != 0) {
+ loop = 800;
+ last_reg = readl(fbi->base + HW_LCDIF_DEBUG0) & DEBUG_VSYNC;
+ do {
+ reg = readl(fbi->base + HW_LCDIF_DEBUG0) & DEBUG_VSYNC;
+ if (reg != last_reg)
+ break;
+ last_reg = reg;
+ loop--;
+ } while (loop != 0);
+ edges--;
+ }
+
+ /* stop FIFO reset */
+ writel(CTRL1_FIFO_CLEAR, fbi->base + HW_LCDIF_CTRL1 + BIT_CLR);
+ /* start the engine right now */
+ writel(CTRL_RUN, fbi->base + HW_LCDIF_CTRL + BIT_SET);
+}
+
+static void stmfb_disable_controller(struct fb_info *fb_info)
+{
+ struct imxfb_info *fbi = fb_info->priv;
+ unsigned loop;
+ uint32_t reg;
+
+ /*
+ * Even if we disable the controller here, it will still continue
+ * until its FIFOs are running out of data
+ */
+ reg = readl(fbi->base + HW_LCDIF_CTRL);
+ reg &= ~CTRL_DOTCLK_MODE;
+ writel(reg, fbi->base + HW_LCDIF_CTRL);
+
+ loop = 1000;
+ while (loop) {
+ reg = readl(fbi->base + HW_LCDIF_CTRL);
+ if (!(reg & CTRL_RUN))
+ break;
+ loop--;
+ }
+
+ reg = readl(fbi->base + HW_LCDIF_VDCTRL4);
+ reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
+ writel(reg, fbi->base + HW_LCDIF_VDCTRL4);
+}
+
+static int stmfb_activate_var(struct fb_info *fb_info)
+{
+ struct imxfb_info *fbi = fb_info->priv;
+ struct imx_fb_videomode *pdata = fbi->pdata;
+ struct fb_videomode *mode = fb_info->mode;
+ uint32_t reg;
+ int ret;
+ unsigned size;
+
+ /*
+ * we need at least this amount of memory for the framebuffer
+ */
+ size = calc_line_length(mode->xres, fb_info->bits_per_pixel) *
+ mode->yres;
+
+ ret = stmfb_memory_mmgt(fb_info, size);
+ if (ret != 0) {
+ dev_err(fbi->hw_dev, "Cannot allocate framebuffer memory\n");
+ return ret;
+ }
+
+ /** @todo ensure HCLK is active at this point of time! */
+
+ size = imx_set_lcdifclk(PICOS2KHZ(mode->pixclock) * 1000);
+ if (size == 0) {
+ dev_dbg(fbi->hw_dev, "Unable to set a valid pixel clock\n");
+ return -EINVAL;
+ }
+
+ /*
+ * bring the controller out of reset and
+ * configure it into DOTCLOCK mode
+ */
+ reg = CTRL_BYPASS_COUNT | /* always in DOTCLOCK mode */
+ CTRL_DOTCLK_MODE;
+ writel(reg, fbi->base + HW_LCDIF_CTRL);
+
+ /* master mode only */
+ reg |= CTRL_MASTER;
+
+ /*
+ * Configure videomode and interface mode
+ */
+ reg |= SET_BUS_WIDTH(pdata->ld_intf_width);
+ switch (fb_info->bits_per_pixel) {
+ case 8:
+ reg |= SET_WORD_LENGTH(1);
+ /** @todo refer manual page 2046 for 8 bpp modes */
+ dev_dbg(fbi->hw_dev, "8 bpp mode not supported yet\n");
+ break;
+ case 16:
+ pr_debug("Setting up an RGB565 mode\n");
+ reg |= SET_WORD_LENGTH(0);
+ reg &= ~CTRL_DF16; /* we assume RGB565 */
+ writel(SET_BYTE_PACKAGING(0xf), fbi->base + HW_LCDIF_CTRL1);
+ fb_info->red = def_rgb565[RED];
+ fb_info->green = def_rgb565[GREEN];
+ fb_info->blue = def_rgb565[BLUE];
+ fb_info->transp = def_rgb565[TRANSP];
+ break;
+ case 24:
+ case 32:
+ pr_debug("Setting up an RGB888/666 mode\n");
+ reg |= SET_WORD_LENGTH(3);
+ switch (pdata->ld_intf_width) {
+ case STMLCDIF_8BIT:
+ dev_dbg(fbi->hw_dev,
+ "Unsupported LCD bus width mapping\n");
+ break;
+ case STMLCDIF_16BIT:
+ case STMLCDIF_18BIT:
+ /* 24 bit to 18 bit mapping
+ * which means: ignore the upper 2 bits in
+ * each colour component
+ */
+ reg |= CTRL_DF24;
+ fb_info->red = def_rgb666[RED];
+ fb_info->green = def_rgb666[GREEN];
+ fb_info->blue = def_rgb666[BLUE];
+ fb_info->transp = def_rgb666[TRANSP];
+ break;
+ case STMLCDIF_24BIT:
+ /* real 24 bit */
+ fb_info->red = def_rgb888[RED];
+ fb_info->green = def_rgb888[GREEN];
+ fb_info->blue = def_rgb888[BLUE];
+ fb_info->transp = def_rgb888[TRANSP];
+ break;
+ }
+ /* do not use packed pixels = one pixel per word instead */
+ writel(SET_BYTE_PACKAGING(0x7), fbi->base + HW_LCDIF_CTRL1);
+ break;
+ default:
+ dev_dbg(fbi->hw_dev, "Unhandled colour depth of %u\n",
+ fb_info->bits_per_pixel);
+ return -EINVAL;
+ }
+ writel(reg, fbi->base + HW_LCDIF_CTRL);
+ pr_debug("Setting up CTRL to %08X\n", reg);
+
+ writel(SET_VCOUNT(mode->yres) |
+ SET_HCOUNT(mode->xres), fbi->base + HW_LCDIF_TRANSFER_COUNT);
+
+ reg = VDCTRL0_ENABLE_PRESENT | /* always in DOTCLOCK mode */
+ VDCTRL0_VSYNC_PERIOD_UNIT |
+ VDCTRL0_VSYNC_PULSE_WIDTH_UNIT;
+ if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+ reg |= VDCTRL0_HSYNC_POL;
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+ reg |= VDCTRL0_VSYNC_POL;
+ if (mode->sync & FB_SYNC_DE_HIGH_ACT)
+ reg |= VDCTRL0_ENABLE_POL;
+ if (mode->sync & FB_SYNC_CLK_INVERT)
+ reg |= VDCTRL0_DOTCLK_POL;
+
+ reg |= SET_VSYNC_PULSE_WIDTH(mode->vsync_len);
+ writel(reg, fbi->base + HW_LCDIF_VDCTRL0);
+ pr_debug("Setting up VDCTRL0 to %08X\n", reg);
+
+ /* frame length in lines */
+ writel(mode->upper_margin + mode->vsync_len + mode->lower_margin +
+ mode->yres,
+ fbi->base + HW_LCDIF_VDCTRL1);
+
+ /* line length in units of clocks or pixels */
+ writel(SET_HSYNC_PULSE_WIDTH(mode->hsync_len) |
+ SET_HSYNC_PERIOD(mode->left_margin + mode->hsync_len +
+ mode->right_margin + mode->xres),
+ fbi->base + HW_LCDIF_VDCTRL2);
+
+ writel(SET_HOR_WAIT_CNT(mode->left_margin + mode->hsync_len) |
+ SET_VERT_WAIT_CNT(mode->upper_margin + mode->vsync_len),
+ fbi->base + HW_LCDIF_VDCTRL3);
+
+ writel(
+#ifdef CONFIG_ARCH_IMX28
+ SET_DOTCLK_DLY(pdata->dotclk_delay) |
+#endif
+ SET_DOTCLK_H_VALID_DATA_CNT(mode->xres),
+ fbi->base + HW_LCDIF_VDCTRL4);
+
+ writel((uint32_t)fb_info->screen_base, fbi->base + HW_LCDIF_CUR_BUF);
+ writel((uint32_t)fb_info->screen_base, fbi->base + HW_LCDIF_NEXT_BUF);
+
+ return 0;
+}
+
+static void stmfb_info(struct device_d *hw_dev)
+{
+ struct imx_fb_videomode *pdata = hw_dev->platform_data;
+ unsigned u;
+
+ printf(" Supported video modes:\n");
+ for (u = 0; u < pdata->mode_cnt; u++)
+ printf(" - '%s': %u x %u\n", pdata->mode_list[u].name,
+ pdata->mode_list[u].xres, pdata->mode_list[u].yres);
+}
+
+/*
+ * There is only one video hardware instance available.
+ * It makes no sense to dynamically allocate this data
+ */
+static struct fb_ops imxfb_ops = {
+ .fb_activate_var = stmfb_activate_var,
+ .fb_enable = stmfb_enable_controller,
+ .fb_disable = stmfb_disable_controller,
+};
+
+static struct imxfb_info fbi = {
+ .info = {
+ .fbops = &imxfb_ops,
+ },
+};
+
+static int stmfb_probe(struct device_d *hw_dev)
+{
+ struct imx_fb_videomode *pdata = hw_dev->platform_data;
+ int ret;
+
+ /* just init */
+ fbi.info.priv = &fbi;
+
+ /* add runtime hardware info */
+ fbi.hw_dev = hw_dev;
+ fbi.base = (void *)hw_dev->map_base;
+ fbi.pdata = pdata;
+
+ /* add runtime video info */
+ fbi.info.mode_list = pdata->mode_list;
+ fbi.info.num_modes = pdata->mode_cnt;
+ fbi.info.mode = &fbi.info.mode_list[0];
+ fbi.info.xres = fbi.info.mode->xres;
+ fbi.info.yres = fbi.info.mode->yres;
+ fbi.info.bits_per_pixel = 16;
+
+ ret = register_framebuffer(&fbi.info);
+ if (ret != 0) {
+ dev_err(hw_dev, "Failed to register framebuffer\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct driver_d stmfb_driver = {
+ .name = "stmfb",
+ .probe = stmfb_probe,
+ .info = stmfb_info,
+};
+
+static int stmfb_init(void)
+{
+ return register_driver(&stmfb_driver);
+}
+
+device_initcall(stmfb_init);
+
+/**
+ * @file
+ * @brief LCDIF driver for i.MX23 and i.MX28
+ *
+ * The LCDIF support four modes of operation
+ * - MPU interface (to drive smart displays) -> not supported yet
+ * - VSYNC interface (like MPU interface plus Vsync) -> not supported yet
+ * - Dotclock interface (to drive LC displays with RGB data and sync signals)
+ * - DVI (to drive ITU-R BT656) -> not supported yet
+ *
+ * This driver depends on a correct setup of the pins used for this purpose
+ * (platform specific).
+ *
+ * For the developer: Don't forget to set the data bus width to the display
+ * in the imx_fb_videomode structure. You will else end up with ugly colours.
+ * If you fight against jitter you can vary the clock delay. This is a feature
+ * of the i.MX28 and you can vary it between 2 ns ... 8 ns in 2 ns steps. Give
+ * the required value in the imx_fb_videomode structure.
+ */