Path: blob/master/arch/arm/mach-footbridge/netwinder-hw.c
26292 views
// SPDX-License-Identifier: GPL-2.01/*2* linux/arch/arm/mach-footbridge/netwinder-hw.c3*4* Netwinder machine fixup5*6* Copyright (C) 1998, 1999 Russell King, Phil Blundell7*/8#include <linux/module.h>9#include <linux/ioport.h>10#include <linux/kernel.h>11#include <linux/delay.h>12#include <linux/init.h>13#include <linux/io.h>14#include <linux/spinlock.h>15#include <linux/slab.h>16#include <linux/leds.h>1718#include <asm/hardware/dec21285.h>19#include <asm/mach-types.h>20#include <asm/setup.h>21#include <asm/system_misc.h>2223#include <asm/mach/arch.h>2425#include "common.h"2627#define IRDA_IO_BASE 0x18028#define GP1_IO_BASE 0x33829#define GP2_IO_BASE 0x33a3031/*32* Winbond WB83977F accessibility stuff33*/34static inline void wb977_open(void)35{36outb(0x87, 0x370);37outb(0x87, 0x370);38}3940static inline void wb977_close(void)41{42outb(0xaa, 0x370);43}4445static inline void wb977_wb(int reg, int val)46{47outb(reg, 0x370);48outb(val, 0x371);49}5051static inline void wb977_ww(int reg, int val)52{53outb(reg, 0x370);54outb(val >> 8, 0x371);55outb(reg + 1, 0x370);56outb(val & 255, 0x371);57}5859#define wb977_device_select(dev) wb977_wb(0x07, dev)60#define wb977_device_disable() wb977_wb(0x30, 0x00)61#define wb977_device_enable() wb977_wb(0x30, 0x01)6263/*64* This is a lock for accessing ports GP1_IO_BASE and GP2_IO_BASE65*/66DEFINE_RAW_SPINLOCK(nw_gpio_lock);67EXPORT_SYMBOL(nw_gpio_lock);6869static unsigned int current_gpio_op;70static unsigned int current_gpio_io;71static unsigned int current_cpld;7273void nw_gpio_modify_op(unsigned int mask, unsigned int set)74{75unsigned int new_gpio, changed;7677new_gpio = (current_gpio_op & ~mask) | set;78changed = new_gpio ^ current_gpio_op;79current_gpio_op = new_gpio;8081if (changed & 0xff)82outb(new_gpio, GP1_IO_BASE);83if (changed & 0xff00)84outb(new_gpio >> 8, GP2_IO_BASE);85}86EXPORT_SYMBOL(nw_gpio_modify_op);8788static inline void __gpio_modify_io(int mask, int in)89{90unsigned int new_gpio, changed;91int port;9293new_gpio = (current_gpio_io & ~mask) | in;94changed = new_gpio ^ current_gpio_io;95current_gpio_io = new_gpio;9697changed >>= 1;98new_gpio >>= 1;99100wb977_device_select(7);101102for (port = 0xe1; changed && port < 0xe8; changed >>= 1) {103wb977_wb(port, new_gpio & 1);104105port += 1;106new_gpio >>= 1;107}108109wb977_device_select(8);110111for (port = 0xe8; changed && port < 0xec; changed >>= 1) {112wb977_wb(port, new_gpio & 1);113114port += 1;115new_gpio >>= 1;116}117}118119void nw_gpio_modify_io(unsigned int mask, unsigned int in)120{121/* Open up the SuperIO chip */122wb977_open();123124__gpio_modify_io(mask, in);125126/* Close up the EFER gate */127wb977_close();128}129EXPORT_SYMBOL(nw_gpio_modify_io);130131unsigned int nw_gpio_read(void)132{133return inb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;134}135EXPORT_SYMBOL(nw_gpio_read);136137/*138* Initialise the Winbond W83977F global registers139*/140static inline void wb977_init_global(void)141{142/*143* Enable R/W config registers144*/145wb977_wb(0x26, 0x40);146147/*148* Power down FDC (not used)149*/150wb977_wb(0x22, 0xfe);151152/*153* GP12, GP11, CIRRX, IRRXH, GP10154*/155wb977_wb(0x2a, 0xc1);156157/*158* GP23, GP22, GP21, GP20, GP13159*/160wb977_wb(0x2b, 0x6b);161162/*163* GP17, GP16, GP15, GP14164*/165wb977_wb(0x2c, 0x55);166}167168/*169* Initialise the Winbond W83977F printer port170*/171static inline void wb977_init_printer(void)172{173wb977_device_select(1);174175/*176* mode 1 == EPP177*/178wb977_wb(0xf0, 0x01);179}180181/*182* Initialise the Winbond W83977F keyboard controller183*/184static inline void wb977_init_keyboard(void)185{186wb977_device_select(5);187188/*189* Keyboard controller address190*/191wb977_ww(0x60, 0x0060);192wb977_ww(0x62, 0x0064);193194/*195* Keyboard IRQ 1, active high, edge trigger196*/197wb977_wb(0x70, 1);198wb977_wb(0x71, 0x02);199200/*201* Mouse IRQ 5, active high, edge trigger202*/203wb977_wb(0x72, 5);204wb977_wb(0x73, 0x02);205206/*207* KBC 8MHz208*/209wb977_wb(0xf0, 0x40);210211/*212* Enable device213*/214wb977_device_enable();215}216217/*218* Initialise the Winbond W83977F Infra-Red device219*/220static inline void wb977_init_irda(void)221{222wb977_device_select(6);223224/*225* IR base address226*/227wb977_ww(0x60, IRDA_IO_BASE);228229/*230* IRDA IRQ 6, active high, edge trigger231*/232wb977_wb(0x70, 6);233wb977_wb(0x71, 0x02);234235/*236* RX DMA - ISA DMA 0237*/238wb977_wb(0x74, 0x00);239240/*241* TX DMA - Disable Tx DMA242*/243wb977_wb(0x75, 0x04);244245/*246* Append CRC, Enable bank selection247*/248wb977_wb(0xf0, 0x03);249250/*251* Enable device252*/253wb977_device_enable();254}255256/*257* Initialise Winbond W83977F general purpose IO258*/259static inline void wb977_init_gpio(void)260{261unsigned long flags;262263/*264* Set up initial I/O definitions265*/266current_gpio_io = -1;267__gpio_modify_io(-1, GPIO_DONE | GPIO_WDTIMER);268269wb977_device_select(7);270271/*272* Group1 base address273*/274wb977_ww(0x60, GP1_IO_BASE);275wb977_ww(0x62, 0);276wb977_ww(0x64, 0);277278/*279* GP10 (Orage button) IRQ 10, active high, edge trigger280*/281wb977_wb(0x70, 10);282wb977_wb(0x71, 0x02);283284/*285* GP10: Debounce filter enabled, IRQ, input286*/287wb977_wb(0xe0, 0x19);288289/*290* Enable Group1291*/292wb977_device_enable();293294wb977_device_select(8);295296/*297* Group2 base address298*/299wb977_ww(0x60, GP2_IO_BASE);300301/*302* Clear watchdog timer regs303* - timer disable304*/305wb977_wb(0xf2, 0x00);306307/*308* - disable LED, no mouse nor keyboard IRQ309*/310wb977_wb(0xf3, 0x00);311312/*313* - timer counting, disable power LED, disable timeouot314*/315wb977_wb(0xf4, 0x00);316317/*318* Enable group2319*/320wb977_device_enable();321322/*323* Set Group1/Group2 outputs324*/325raw_spin_lock_irqsave(&nw_gpio_lock, flags);326nw_gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN);327raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);328}329330/*331* Initialise the Winbond W83977F chip.332*/333static void __init wb977_init(void)334{335request_region(0x370, 2, "W83977AF configuration");336337/*338* Open up the SuperIO chip339*/340wb977_open();341342/*343* Initialise the global registers344*/345wb977_init_global();346347/*348* Initialise the various devices in349* the multi-IO chip.350*/351wb977_init_printer();352wb977_init_keyboard();353wb977_init_irda();354wb977_init_gpio();355356/*357* Close up the EFER gate358*/359wb977_close();360}361362void nw_cpld_modify(unsigned int mask, unsigned int set)363{364int msk;365366current_cpld = (current_cpld & ~mask) | set;367368nw_gpio_modify_io(GPIO_DATA | GPIO_IOCLK | GPIO_IOLOAD, 0);369nw_gpio_modify_op(GPIO_IOLOAD, 0);370371for (msk = 8; msk; msk >>= 1) {372int bit = current_cpld & msk;373374nw_gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0);375nw_gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK);376}377378nw_gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0);379nw_gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK);380nw_gpio_modify_op(GPIO_IOLOAD, 0);381}382EXPORT_SYMBOL(nw_cpld_modify);383384static void __init cpld_init(void)385{386unsigned long flags;387388raw_spin_lock_irqsave(&nw_gpio_lock, flags);389nw_cpld_modify(-1, CPLD_UNMUTE | CPLD_7111_DISABLE);390raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);391}392393static unsigned char rwa_unlock[] __initdata =394{ 0x00, 0x00, 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b,3950x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74,3960x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 };397398#ifndef DEBUG399#define dprintk(x...)400#else401#define dprintk(x...) printk(x)402#endif403404#define WRITE_RWA(r,v) do { outb((r), 0x279); udelay(10); outb((v), 0xa79); } while (0)405406static inline void rwa010_unlock(void)407{408int i;409410WRITE_RWA(2, 2);411mdelay(10);412413for (i = 0; i < sizeof(rwa_unlock); i++) {414outb(rwa_unlock[i], 0x279);415udelay(10);416}417}418419static inline void rwa010_read_ident(void)420{421unsigned char si[9];422int i, j;423424WRITE_RWA(3, 0);425WRITE_RWA(0, 128);426427outb(1, 0x279);428429mdelay(1);430431dprintk("Identifier: ");432for (i = 0; i < 9; i++) {433si[i] = 0;434for (j = 0; j < 8; j++) {435int bit;436udelay(250);437inb(0x203);438udelay(250);439bit = inb(0x203);440dprintk("%02X ", bit);441bit = (bit == 0xaa) ? 1 : 0;442si[i] |= bit << j;443}444dprintk("(%02X) ", si[i]);445}446dprintk("\n");447}448449static inline void rwa010_global_init(void)450{451WRITE_RWA(6, 2); // Assign a card no = 2452453dprintk("Card no = %d\n", inb(0x203));454455/* disable the modem section of the chip */456WRITE_RWA(7, 3);457WRITE_RWA(0x30, 0);458459/* disable the cdrom section of the chip */460WRITE_RWA(7, 4);461WRITE_RWA(0x30, 0);462463/* disable the MPU-401 section of the chip */464WRITE_RWA(7, 2);465WRITE_RWA(0x30, 0);466}467468static inline void rwa010_game_port_init(void)469{470int i;471472WRITE_RWA(7, 5);473474dprintk("Slider base: ");475WRITE_RWA(0x61, 1);476i = inb(0x203);477478WRITE_RWA(0x60, 2);479dprintk("%02X%02X (201)\n", inb(0x203), i);480481WRITE_RWA(0x30, 1);482}483484static inline void rwa010_waveartist_init(int base, int irq, int dma)485{486int i;487488WRITE_RWA(7, 0);489490dprintk("WaveArtist base: ");491WRITE_RWA(0x61, base & 255);492i = inb(0x203);493494WRITE_RWA(0x60, base >> 8);495dprintk("%02X%02X (%X),", inb(0x203), i, base);496497WRITE_RWA(0x70, irq);498dprintk(" irq: %d (%d),", inb(0x203), irq);499500WRITE_RWA(0x74, dma);501dprintk(" dma: %d (%d)\n", inb(0x203), dma);502503WRITE_RWA(0x30, 1);504}505506static inline void rwa010_soundblaster_init(int sb_base, int al_base, int irq, int dma)507{508int i;509510WRITE_RWA(7, 1);511512dprintk("SoundBlaster base: ");513WRITE_RWA(0x61, sb_base & 255);514i = inb(0x203);515516WRITE_RWA(0x60, sb_base >> 8);517dprintk("%02X%02X (%X),", inb(0x203), i, sb_base);518519dprintk(" irq: ");520WRITE_RWA(0x70, irq);521dprintk("%d (%d),", inb(0x203), irq);522523dprintk(" 8-bit DMA: ");524WRITE_RWA(0x74, dma);525dprintk("%d (%d)\n", inb(0x203), dma);526527dprintk("AdLib base: ");528WRITE_RWA(0x63, al_base & 255);529i = inb(0x203);530531WRITE_RWA(0x62, al_base >> 8);532dprintk("%02X%02X (%X)\n", inb(0x203), i, al_base);533534WRITE_RWA(0x30, 1);535}536537static void rwa010_soundblaster_reset(void)538{539int i;540541outb(1, 0x226);542udelay(3);543outb(0, 0x226);544545for (i = 0; i < 5; i++) {546if (inb(0x22e) & 0x80)547break;548mdelay(1);549}550if (i == 5)551printk("SoundBlaster: DSP reset failed\n");552553dprintk("SoundBlaster DSP reset: %02X (AA)\n", inb(0x22a));554555for (i = 0; i < 5; i++) {556if ((inb(0x22c) & 0x80) == 0)557break;558mdelay(1);559}560561if (i == 5)562printk("SoundBlaster: DSP not ready\n");563else {564outb(0xe1, 0x22c);565566dprintk("SoundBlaster DSP id: ");567i = inb(0x22a);568udelay(1);569i |= inb(0x22a) << 8;570dprintk("%04X\n", i);571572for (i = 0; i < 5; i++) {573if ((inb(0x22c) & 0x80) == 0)574break;575mdelay(1);576}577578if (i == 5)579printk("SoundBlaster: could not turn speaker off\n");580581outb(0xd3, 0x22c);582}583584/* turn on OPL3 */585outb(5, 0x38a);586outb(1, 0x38b);587}588589static void __init rwa010_init(void)590{591rwa010_unlock();592rwa010_read_ident();593rwa010_global_init();594rwa010_game_port_init();595rwa010_waveartist_init(0x250, 3, 7);596rwa010_soundblaster_init(0x220, 0x388, 3, 1);597rwa010_soundblaster_reset();598}599600/*601* Initialise any other hardware after we've got the PCI bus602* initialised. We may need the PCI bus to talk to this other603* hardware.604*/605static int __init nw_hw_init(void)606{607if (machine_is_netwinder()) {608wb977_init();609cpld_init();610rwa010_init();611}612return 0;613}614615__initcall(nw_hw_init);616617/*618* Older NeTTroms either do not provide a parameters619* page, or they don't supply correct information in620* the parameter page.621*/622static void __init623fixup_netwinder(struct tag *tags, char **cmdline)624{625#ifdef CONFIG_ISAPNP626extern int isapnp_disable;627628/*629* We must not use the kernels ISAPnP code630* on the NetWinder - it will reset the settings631* for the WaveArtist chip and render it inoperable.632*/633isapnp_disable = 1;634#endif635}636637static void netwinder_restart(enum reboot_mode mode, const char *cmd)638{639if (mode == REBOOT_SOFT) {640/* Jump into the ROM */641soft_restart(0x41000000);642} else {643local_irq_disable();644local_fiq_disable();645646/* open up the SuperIO chip */647outb(0x87, 0x370);648outb(0x87, 0x370);649650/* aux function group 1 (logical device 7) */651outb(0x07, 0x370);652outb(0x07, 0x371);653654/* set GP16 for WD-TIMER output */655outb(0xe6, 0x370);656outb(0x00, 0x371);657658/* set a RED LED and toggle WD_TIMER for rebooting */659outb(0xc4, 0x338);660}661}662663/* LEDs */664#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)665struct netwinder_led {666struct led_classdev cdev;667u8 mask;668};669670/*671* The triggers lines up below will only be used if the672* LED triggers are compiled in.673*/674static const struct {675const char *name;676const char *trigger;677} netwinder_leds[] = {678{ "netwinder:green", "heartbeat", },679{ "netwinder:red", "cpu0", },680};681682/*683* The LED control in Netwinder is reversed:684* - setting bit means turn off LED685* - clearing bit means turn on LED686*/687static void netwinder_led_set(struct led_classdev *cdev,688enum led_brightness b)689{690struct netwinder_led *led = container_of(cdev,691struct netwinder_led, cdev);692unsigned long flags;693u32 reg;694695raw_spin_lock_irqsave(&nw_gpio_lock, flags);696reg = nw_gpio_read();697if (b != LED_OFF)698reg &= ~led->mask;699else700reg |= led->mask;701nw_gpio_modify_op(led->mask, reg);702raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);703}704705static enum led_brightness netwinder_led_get(struct led_classdev *cdev)706{707struct netwinder_led *led = container_of(cdev,708struct netwinder_led, cdev);709unsigned long flags;710u32 reg;711712raw_spin_lock_irqsave(&nw_gpio_lock, flags);713reg = nw_gpio_read();714raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);715716return (reg & led->mask) ? LED_OFF : LED_FULL;717}718719static int __init netwinder_leds_init(void)720{721int i;722723if (!machine_is_netwinder())724return -ENODEV;725726for (i = 0; i < ARRAY_SIZE(netwinder_leds); i++) {727struct netwinder_led *led;728729led = kzalloc(sizeof(*led), GFP_KERNEL);730if (!led)731break;732733led->cdev.name = netwinder_leds[i].name;734led->cdev.brightness_set = netwinder_led_set;735led->cdev.brightness_get = netwinder_led_get;736led->cdev.default_trigger = netwinder_leds[i].trigger;737738if (i == 0)739led->mask = GPIO_GREEN_LED;740else741led->mask = GPIO_RED_LED;742743if (led_classdev_register(NULL, &led->cdev) < 0) {744kfree(led);745break;746}747}748749return 0;750}751752/*753* Since we may have triggers on any subsystem, defer registration754* until after subsystem_init.755*/756fs_initcall(netwinder_leds_init);757#endif758759MACHINE_START(NETWINDER, "Rebel-NetWinder")760/* Maintainer: Russell King/Rebel.com */761.atag_offset = 0x100,762.video_start = 0x000a0000,763.video_end = 0x000bffff,764.reserve_lp0 = 1,765.reserve_lp2 = 1,766.fixup = fixup_netwinder,767.map_io = footbridge_map_io,768.init_irq = footbridge_init_irq,769.init_time = isa_timer_init,770.restart = netwinder_restart,771MACHINE_END772773774