|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * devoard misc stuff. | 
|  | */ | 
|  |  | 
|  | #include <linux/init.h> | 
|  | #include <linux/mtd/mtd.h> | 
|  | #include <linux/mtd/map.h> | 
|  | #include <linux/mtd/physmap.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/pm.h> | 
|  |  | 
|  | #include <asm/bootinfo.h> | 
|  | #include <asm/idle.h> | 
|  | #include <asm/reboot.h> | 
|  | #include <asm/setup.h> | 
|  | #include <asm/mach-au1x00/au1000.h> | 
|  | #include <asm/mach-db1x00/bcsr.h> | 
|  |  | 
|  | #include <prom.h> | 
|  |  | 
|  | void prom_putchar(char c) | 
|  | { | 
|  | if (alchemy_get_cputype() == ALCHEMY_CPU_AU1300) | 
|  | alchemy_uart_putchar(AU1300_UART2_PHYS_ADDR, c); | 
|  | else | 
|  | alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c); | 
|  | } | 
|  |  | 
|  |  | 
|  | static struct platform_device db1x00_rtc_dev = { | 
|  | .name	= "rtc-au1xxx", | 
|  | .id	= -1, | 
|  | }; | 
|  |  | 
|  |  | 
|  | static void db1x_power_off(void) | 
|  | { | 
|  | bcsr_write(BCSR_RESETS, 0); | 
|  | bcsr_write(BCSR_SYSTEM, BCSR_SYSTEM_PWROFF | BCSR_SYSTEM_RESET); | 
|  | while (1)		/* sit and spin */ | 
|  | cpu_wait(); | 
|  | } | 
|  |  | 
|  | static void db1x_reset(char *c) | 
|  | { | 
|  | bcsr_write(BCSR_RESETS, 0); | 
|  | bcsr_write(BCSR_SYSTEM, 0); | 
|  | } | 
|  |  | 
|  | static int __init db1x_late_setup(void) | 
|  | { | 
|  | if (!pm_power_off) | 
|  | pm_power_off = db1x_power_off; | 
|  | if (!_machine_halt) | 
|  | _machine_halt = db1x_power_off; | 
|  | if (!_machine_restart) | 
|  | _machine_restart = db1x_reset; | 
|  |  | 
|  | platform_device_register(&db1x00_rtc_dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | device_initcall(db1x_late_setup); | 
|  |  | 
|  | /* register a pcmcia socket */ | 
|  | int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start, | 
|  | phys_addr_t pcmcia_attr_end, | 
|  | phys_addr_t pcmcia_mem_start, | 
|  | phys_addr_t pcmcia_mem_end, | 
|  | phys_addr_t pcmcia_io_start, | 
|  | phys_addr_t pcmcia_io_end, | 
|  | int card_irq, | 
|  | int cd_irq, | 
|  | int stschg_irq, | 
|  | int eject_irq, | 
|  | int id) | 
|  | { | 
|  | int cnt, i, ret; | 
|  | struct resource *sr; | 
|  | struct platform_device *pd; | 
|  |  | 
|  | cnt = 5; | 
|  | if (eject_irq) | 
|  | cnt++; | 
|  | if (stschg_irq) | 
|  | cnt++; | 
|  |  | 
|  | sr = kcalloc(cnt, sizeof(struct resource), GFP_KERNEL); | 
|  | if (!sr) | 
|  | return -ENOMEM; | 
|  |  | 
|  | pd = platform_device_alloc("db1xxx_pcmcia", id); | 
|  | if (!pd) { | 
|  | ret = -ENOMEM; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | sr[0].name	= "pcmcia-attr"; | 
|  | sr[0].flags	= IORESOURCE_MEM; | 
|  | sr[0].start	= pcmcia_attr_start; | 
|  | sr[0].end	= pcmcia_attr_end; | 
|  |  | 
|  | sr[1].name	= "pcmcia-mem"; | 
|  | sr[1].flags	= IORESOURCE_MEM; | 
|  | sr[1].start	= pcmcia_mem_start; | 
|  | sr[1].end	= pcmcia_mem_end; | 
|  |  | 
|  | sr[2].name	= "pcmcia-io"; | 
|  | sr[2].flags	= IORESOURCE_MEM; | 
|  | sr[2].start	= pcmcia_io_start; | 
|  | sr[2].end	= pcmcia_io_end; | 
|  |  | 
|  | sr[3].name	= "insert"; | 
|  | sr[3].flags	= IORESOURCE_IRQ; | 
|  | sr[3].start = sr[3].end = cd_irq; | 
|  |  | 
|  | sr[4].name	= "card"; | 
|  | sr[4].flags	= IORESOURCE_IRQ; | 
|  | sr[4].start = sr[4].end = card_irq; | 
|  |  | 
|  | i = 5; | 
|  | if (stschg_irq) { | 
|  | sr[i].name	= "stschg"; | 
|  | sr[i].flags	= IORESOURCE_IRQ; | 
|  | sr[i].start = sr[i].end = stschg_irq; | 
|  | i++; | 
|  | } | 
|  | if (eject_irq) { | 
|  | sr[i].name	= "eject"; | 
|  | sr[i].flags	= IORESOURCE_IRQ; | 
|  | sr[i].start = sr[i].end = eject_irq; | 
|  | } | 
|  |  | 
|  | pd->resource = sr; | 
|  | pd->num_resources = cnt; | 
|  |  | 
|  | ret = platform_device_add(pd); | 
|  | if (!ret) | 
|  | return 0; | 
|  |  | 
|  | platform_device_put(pd); | 
|  | out: | 
|  | kfree(sr); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #define YAMON_SIZE	0x00100000 | 
|  | #define YAMON_ENV_SIZE	0x00040000 | 
|  |  | 
|  | int __init db1x_register_norflash(unsigned long size, int width, | 
|  | int swapped) | 
|  | { | 
|  | struct physmap_flash_data *pfd; | 
|  | struct platform_device *pd; | 
|  | struct mtd_partition *parts; | 
|  | struct resource *res; | 
|  | int ret, i; | 
|  |  | 
|  | if (size < (8 * 1024 * 1024)) | 
|  | return -EINVAL; | 
|  |  | 
|  | ret = -ENOMEM; | 
|  | parts = kcalloc(5, sizeof(struct mtd_partition), GFP_KERNEL); | 
|  | if (!parts) | 
|  | goto out; | 
|  |  | 
|  | res = kzalloc(sizeof(struct resource), GFP_KERNEL); | 
|  | if (!res) | 
|  | goto out1; | 
|  |  | 
|  | pfd = kzalloc(sizeof(struct physmap_flash_data), GFP_KERNEL); | 
|  | if (!pfd) | 
|  | goto out2; | 
|  |  | 
|  | pd = platform_device_alloc("physmap-flash", 0); | 
|  | if (!pd) | 
|  | goto out3; | 
|  |  | 
|  | /* NOR flash ends at 0x20000000, regardless of size */ | 
|  | res->start = 0x20000000 - size; | 
|  | res->end = 0x20000000 - 1; | 
|  | res->flags = IORESOURCE_MEM; | 
|  |  | 
|  | /* partition setup.  Most Develboards have a switch which allows | 
|  | * to swap the physical locations of the 2 NOR flash banks. | 
|  | */ | 
|  | i = 0; | 
|  | if (!swapped) { | 
|  | /* first NOR chip */ | 
|  | parts[i].offset = 0; | 
|  | parts[i].name = "User FS"; | 
|  | parts[i].size = size / 2; | 
|  | i++; | 
|  | } | 
|  |  | 
|  | parts[i].offset = MTDPART_OFS_APPEND; | 
|  | parts[i].name = "User FS 2"; | 
|  | parts[i].size = (size / 2) - (0x20000000 - 0x1fc00000); | 
|  | i++; | 
|  |  | 
|  | parts[i].offset = MTDPART_OFS_APPEND; | 
|  | parts[i].name = "YAMON"; | 
|  | parts[i].size = YAMON_SIZE; | 
|  | parts[i].mask_flags = MTD_WRITEABLE; | 
|  | i++; | 
|  |  | 
|  | parts[i].offset = MTDPART_OFS_APPEND; | 
|  | parts[i].name = "raw kernel"; | 
|  | parts[i].size = 0x00400000 - YAMON_SIZE - YAMON_ENV_SIZE; | 
|  | i++; | 
|  |  | 
|  | parts[i].offset = MTDPART_OFS_APPEND; | 
|  | parts[i].name = "YAMON Env"; | 
|  | parts[i].size = YAMON_ENV_SIZE; | 
|  | parts[i].mask_flags = MTD_WRITEABLE; | 
|  | i++; | 
|  |  | 
|  | if (swapped) { | 
|  | parts[i].offset = MTDPART_OFS_APPEND; | 
|  | parts[i].name = "User FS"; | 
|  | parts[i].size = size / 2; | 
|  | i++; | 
|  | } | 
|  |  | 
|  | pfd->width = width; | 
|  | pfd->parts = parts; | 
|  | pfd->nr_parts = 5; | 
|  |  | 
|  | pd->dev.platform_data = pfd; | 
|  | pd->resource = res; | 
|  | pd->num_resources = 1; | 
|  |  | 
|  | ret = platform_device_add(pd); | 
|  | if (!ret) | 
|  | return ret; | 
|  |  | 
|  | platform_device_put(pd); | 
|  | out3: | 
|  | kfree(pfd); | 
|  | out2: | 
|  | kfree(res); | 
|  | out1: | 
|  | kfree(parts); | 
|  | out: | 
|  | return ret; | 
|  | } |