mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
2fb04fdf30
Commitb70661c708
("net: smc91x: use run-time configuration on all ARM machines") broke some ARM platforms through several mistakes. Firstly, the access size must correspond to the following rule: (a) at least one of 16-bit or 8-bit access size must be supported (b) 32-bit accesses are optional, and may be enabled in addition to the above. Secondly, it provides no emulation of 16-bit accesses, instead blindly making 16-bit accesses even when the platform specifies that only 8-bit is supported. Reorganise smc91x.h so we can make use of the existing 16-bit access emulation already provided - if 16-bit accesses are supported, use 16-bit accesses directly, otherwise if 8-bit accesses are supported, use the provided 16-bit access emulation. If neither, BUG(). This exactly reflects the driver behaviour prior to the commit being fixed. Since the conversion incorrectly cut down the available access sizes on several platforms, we also need to go through every platform and fix up the overly-restrictive access size: Arnd assumed that if a platform can perform 32-bit, 16-bit and 8-bit accesses, then only a 32-bit access size needed to be specified - not so, all available access sizes must be specified. This likely fixes some performance regressions in doing this: if a platform does not support 8-bit accesses, 8-bit accesses have been emulated by performing a 16-bit read-modify-write access. Tested on the Intel Assabet/Neponset platform, which supports only 8-bit accesses, which was broken by the original commit. Fixes:b70661c708
("net: smc91x: use run-time configuration on all ARM machines") Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Tested-by: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
293 lines
6.1 KiB
C
293 lines
6.1 KiB
C
/*
|
|
* linux/arch/arm/mach-pxa/idp.c
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Copyright (c) 2001 Cliff Brake, Accelent Systems Inc.
|
|
*
|
|
* 2001-09-13: Cliff Brake <cbrake@accelent.com>
|
|
* Initial code
|
|
*
|
|
* 2005-02-15: Cliff Brake <cliff.brake@gmail.com>
|
|
* <http://www.vibren.com> <http://bec-systems.com>
|
|
* Updated for 2.6 kernel
|
|
*
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/fb.h>
|
|
|
|
#include <asm/setup.h>
|
|
#include <asm/memory.h>
|
|
#include <asm/mach-types.h>
|
|
#include <mach/hardware.h>
|
|
#include <asm/irq.h>
|
|
|
|
#include <asm/mach/arch.h>
|
|
#include <asm/mach/map.h>
|
|
|
|
#include "pxa25x.h"
|
|
#include "idp.h"
|
|
#include <linux/platform_data/video-pxafb.h>
|
|
#include <mach/bitfield.h>
|
|
#include <linux/platform_data/mmc-pxamci.h>
|
|
#include <linux/smc91x.h>
|
|
|
|
#include "generic.h"
|
|
#include "devices.h"
|
|
|
|
/* TODO:
|
|
* - add pxa2xx_audio_ops_t device structure
|
|
* - Ethernet interrupt
|
|
*/
|
|
|
|
static unsigned long idp_pin_config[] __initdata = {
|
|
/* LCD */
|
|
GPIOxx_LCD_DSTN_16BPP,
|
|
|
|
/* BTUART */
|
|
GPIO42_BTUART_RXD,
|
|
GPIO43_BTUART_TXD,
|
|
GPIO44_BTUART_CTS,
|
|
GPIO45_BTUART_RTS,
|
|
|
|
/* STUART */
|
|
GPIO46_STUART_RXD,
|
|
GPIO47_STUART_TXD,
|
|
|
|
/* MMC */
|
|
GPIO6_MMC_CLK,
|
|
GPIO8_MMC_CS0,
|
|
|
|
/* Ethernet */
|
|
GPIO33_nCS_5, /* Ethernet CS */
|
|
GPIO4_GPIO, /* Ethernet IRQ */
|
|
};
|
|
|
|
static struct resource smc91x_resources[] = {
|
|
[0] = {
|
|
.start = (IDP_ETH_PHYS + 0x300),
|
|
.end = (IDP_ETH_PHYS + 0xfffff),
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
[1] = {
|
|
.start = PXA_GPIO_TO_IRQ(4),
|
|
.end = PXA_GPIO_TO_IRQ(4),
|
|
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
|
|
}
|
|
};
|
|
|
|
static struct smc91x_platdata smc91x_platdata = {
|
|
.flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
|
|
SMC91X_USE_DMA | SMC91X_NOWAIT,
|
|
};
|
|
|
|
static struct platform_device smc91x_device = {
|
|
.name = "smc91x",
|
|
.id = 0,
|
|
.num_resources = ARRAY_SIZE(smc91x_resources),
|
|
.resource = smc91x_resources,
|
|
.dev.platform_data = &smc91x_platdata,
|
|
};
|
|
|
|
static void idp_backlight_power(int on)
|
|
{
|
|
if (on) {
|
|
IDP_CPLD_LCD |= (1<<1);
|
|
} else {
|
|
IDP_CPLD_LCD &= ~(1<<1);
|
|
}
|
|
}
|
|
|
|
static void idp_vlcd(int on)
|
|
{
|
|
if (on) {
|
|
IDP_CPLD_LCD |= (1<<2);
|
|
} else {
|
|
IDP_CPLD_LCD &= ~(1<<2);
|
|
}
|
|
}
|
|
|
|
static void idp_lcd_power(int on, struct fb_var_screeninfo *var)
|
|
{
|
|
if (on) {
|
|
IDP_CPLD_LCD |= (1<<0);
|
|
} else {
|
|
IDP_CPLD_LCD &= ~(1<<0);
|
|
}
|
|
|
|
/* call idp_vlcd for now as core driver does not support
|
|
* both power and vlcd hooks. Note, this is not technically
|
|
* the correct sequence, but seems to work. Disclaimer:
|
|
* this may eventually damage the display.
|
|
*/
|
|
|
|
idp_vlcd(on);
|
|
}
|
|
|
|
static struct pxafb_mode_info sharp_lm8v31_mode = {
|
|
.pixclock = 270000,
|
|
.xres = 640,
|
|
.yres = 480,
|
|
.bpp = 16,
|
|
.hsync_len = 1,
|
|
.left_margin = 3,
|
|
.right_margin = 3,
|
|
.vsync_len = 1,
|
|
.upper_margin = 0,
|
|
.lower_margin = 0,
|
|
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
|
|
.cmap_greyscale = 0,
|
|
};
|
|
|
|
static struct pxafb_mach_info sharp_lm8v31 = {
|
|
.modes = &sharp_lm8v31_mode,
|
|
.num_modes = 1,
|
|
.cmap_inverse = 0,
|
|
.cmap_static = 0,
|
|
.lcd_conn = LCD_COLOR_DSTN_16BPP | LCD_PCLK_EDGE_FALL |
|
|
LCD_AC_BIAS_FREQ(255),
|
|
.pxafb_backlight_power = &idp_backlight_power,
|
|
.pxafb_lcd_power = &idp_lcd_power
|
|
};
|
|
|
|
static struct pxamci_platform_data idp_mci_platform_data = {
|
|
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
|
|
.gpio_card_detect = -1,
|
|
.gpio_card_ro = -1,
|
|
.gpio_power = -1,
|
|
};
|
|
|
|
static void __init idp_init(void)
|
|
{
|
|
printk("idp_init()\n");
|
|
|
|
pxa2xx_mfp_config(ARRAY_AND_SIZE(idp_pin_config));
|
|
pxa_set_ffuart_info(NULL);
|
|
pxa_set_btuart_info(NULL);
|
|
pxa_set_stuart_info(NULL);
|
|
|
|
platform_device_register(&smc91x_device);
|
|
//platform_device_register(&mst_audio_device);
|
|
pxa_set_fb_info(NULL, &sharp_lm8v31);
|
|
pxa_set_mci_info(&idp_mci_platform_data);
|
|
}
|
|
|
|
static struct map_desc idp_io_desc[] __initdata = {
|
|
{
|
|
.virtual = IDP_COREVOLT_VIRT,
|
|
.pfn = __phys_to_pfn(IDP_COREVOLT_PHYS),
|
|
.length = IDP_COREVOLT_SIZE,
|
|
.type = MT_DEVICE
|
|
}, {
|
|
.virtual = IDP_CPLD_VIRT,
|
|
.pfn = __phys_to_pfn(IDP_CPLD_PHYS),
|
|
.length = IDP_CPLD_SIZE,
|
|
.type = MT_DEVICE
|
|
}
|
|
};
|
|
|
|
static void __init idp_map_io(void)
|
|
{
|
|
pxa25x_map_io();
|
|
iotable_init(idp_io_desc, ARRAY_SIZE(idp_io_desc));
|
|
}
|
|
|
|
/* LEDs */
|
|
#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
|
|
struct idp_led {
|
|
struct led_classdev cdev;
|
|
u8 mask;
|
|
};
|
|
|
|
/*
|
|
* The triggers lines up below will only be used if the
|
|
* LED triggers are compiled in.
|
|
*/
|
|
static const struct {
|
|
const char *name;
|
|
const char *trigger;
|
|
} idp_leds[] = {
|
|
{ "idp:green", "heartbeat", },
|
|
{ "idp:red", "cpu0", },
|
|
};
|
|
|
|
static void idp_led_set(struct led_classdev *cdev,
|
|
enum led_brightness b)
|
|
{
|
|
struct idp_led *led = container_of(cdev,
|
|
struct idp_led, cdev);
|
|
u32 reg = IDP_CPLD_LED_CONTROL;
|
|
|
|
if (b != LED_OFF)
|
|
reg &= ~led->mask;
|
|
else
|
|
reg |= led->mask;
|
|
|
|
IDP_CPLD_LED_CONTROL = reg;
|
|
}
|
|
|
|
static enum led_brightness idp_led_get(struct led_classdev *cdev)
|
|
{
|
|
struct idp_led *led = container_of(cdev,
|
|
struct idp_led, cdev);
|
|
|
|
return (IDP_CPLD_LED_CONTROL & led->mask) ? LED_OFF : LED_FULL;
|
|
}
|
|
|
|
static int __init idp_leds_init(void)
|
|
{
|
|
int i;
|
|
|
|
if (!machine_is_pxa_idp())
|
|
return -ENODEV;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(idp_leds); i++) {
|
|
struct idp_led *led;
|
|
|
|
led = kzalloc(sizeof(*led), GFP_KERNEL);
|
|
if (!led)
|
|
break;
|
|
|
|
led->cdev.name = idp_leds[i].name;
|
|
led->cdev.brightness_set = idp_led_set;
|
|
led->cdev.brightness_get = idp_led_get;
|
|
led->cdev.default_trigger = idp_leds[i].trigger;
|
|
|
|
if (i == 0)
|
|
led->mask = IDP_HB_LED;
|
|
else
|
|
led->mask = IDP_BUSY_LED;
|
|
|
|
if (led_classdev_register(NULL, &led->cdev) < 0) {
|
|
kfree(led);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Since we may have triggers on any subsystem, defer registration
|
|
* until after subsystem_init.
|
|
*/
|
|
fs_initcall(idp_leds_init);
|
|
#endif
|
|
|
|
MACHINE_START(PXA_IDP, "Vibren PXA255 IDP")
|
|
/* Maintainer: Vibren Technologies */
|
|
.map_io = idp_map_io,
|
|
.nr_irqs = PXA_NR_IRQS,
|
|
.init_irq = pxa25x_init_irq,
|
|
.handle_irq = pxa25x_handle_irq,
|
|
.init_time = pxa_timer_init,
|
|
.init_machine = idp_init,
|
|
.restart = pxa_restart,
|
|
MACHINE_END
|