diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Kconfig | 1 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/led/Kconfig | 21 | ||||
-rw-r--r-- | drivers/led/Makefile | 3 | ||||
-rw-r--r-- | drivers/led/core.c | 157 | ||||
-rw-r--r-- | drivers/led/led-gpio.c | 94 | ||||
-rw-r--r-- | drivers/led/led-triggers.c | 153 | ||||
-rw-r--r-- | drivers/mci/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mci/stm378x.c | 143 | ||||
-rw-r--r-- | drivers/net/fec_imx.c | 95 | ||||
-rw-r--r-- | drivers/net/fec_imx.h | 23 | ||||
-rw-r--r-- | drivers/net/netx_eth.c | 1 | ||||
-rw-r--r-- | drivers/nor/cfi_flash.c | 143 | ||||
-rw-r--r-- | drivers/nor/cfi_flash.h | 25 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 2 | ||||
-rw-r--r-- | drivers/serial/stm-serial.c | 9 | ||||
-rw-r--r-- | drivers/spi/imx_spi.c | 3 | ||||
-rw-r--r-- | drivers/video/Kconfig | 7 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/fb.c | 44 | ||||
-rw-r--r-- | drivers/video/stm.c | 540 |
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. + */ |