18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * DSM-G600 board-setup
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au>
68c2ecf20Sopenharmony_ci * Copyright (C) 2006 Tower Technologies
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * based on ixdp425-setup.c:
98c2ecf20Sopenharmony_ci *      Copyright (C) 2003-2004 MontaVista Software, Inc.
108c2ecf20Sopenharmony_ci * based on nslu2-power.c:
118c2ecf20Sopenharmony_ci *	Copyright (C) 2005 Tower Technologies
128c2ecf20Sopenharmony_ci * based on nslu2-io.c:
138c2ecf20Sopenharmony_ci *	Copyright (C) 2004 Karen Spearel
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Author: Alessandro Zummo <a.zummo@towertech.it>
168c2ecf20Sopenharmony_ci * Author: Michael Westerhof <mwester@dls.net>
178c2ecf20Sopenharmony_ci * Author: Rod Whitby <rod@whitby.id.au>
188c2ecf20Sopenharmony_ci * Maintainers: http://www.nslu2-linux.org/
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci#include <linux/gpio.h>
218c2ecf20Sopenharmony_ci#include <linux/irq.h>
228c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
238c2ecf20Sopenharmony_ci#include <linux/timer.h>
248c2ecf20Sopenharmony_ci#include <linux/serial.h>
258c2ecf20Sopenharmony_ci#include <linux/serial_8250.h>
268c2ecf20Sopenharmony_ci#include <linux/leds.h>
278c2ecf20Sopenharmony_ci#include <linux/reboot.h>
288c2ecf20Sopenharmony_ci#include <linux/i2c.h>
298c2ecf20Sopenharmony_ci#include <linux/gpio/machine.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <mach/hardware.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
348c2ecf20Sopenharmony_ci#include <asm/mach/arch.h>
358c2ecf20Sopenharmony_ci#include <asm/mach/flash.h>
368c2ecf20Sopenharmony_ci#include <asm/mach/time.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include "irqs.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define DSMG600_SDA_PIN		5
418c2ecf20Sopenharmony_ci#define DSMG600_SCL_PIN		4
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* DSM-G600 Timer Setting */
448c2ecf20Sopenharmony_ci#define DSMG600_FREQ		66000000
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* Buttons */
478c2ecf20Sopenharmony_ci#define DSMG600_PB_GPIO		15	/* power button */
488c2ecf20Sopenharmony_ci#define DSMG600_RB_GPIO		3	/* reset button */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Power control */
518c2ecf20Sopenharmony_ci#define DSMG600_PO_GPIO		2	/* power off */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* LEDs */
548c2ecf20Sopenharmony_ci#define DSMG600_LED_PWR_GPIO	0
558c2ecf20Sopenharmony_ci#define DSMG600_LED_WLAN_GPIO	14
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic struct flash_platform_data dsmg600_flash_data = {
588c2ecf20Sopenharmony_ci	.map_name		= "cfi_probe",
598c2ecf20Sopenharmony_ci	.width			= 2,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct resource dsmg600_flash_resource = {
638c2ecf20Sopenharmony_ci	.flags			= IORESOURCE_MEM,
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct platform_device dsmg600_flash = {
678c2ecf20Sopenharmony_ci	.name			= "IXP4XX-Flash",
688c2ecf20Sopenharmony_ci	.id			= 0,
698c2ecf20Sopenharmony_ci	.dev.platform_data	= &dsmg600_flash_data,
708c2ecf20Sopenharmony_ci	.num_resources		= 1,
718c2ecf20Sopenharmony_ci	.resource		= &dsmg600_flash_resource,
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic struct gpiod_lookup_table dsmg600_i2c_gpiod_table = {
758c2ecf20Sopenharmony_ci	.dev_id		= "i2c-gpio.0",
768c2ecf20Sopenharmony_ci	.table		= {
778c2ecf20Sopenharmony_ci		GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", DSMG600_SDA_PIN,
788c2ecf20Sopenharmony_ci				NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
798c2ecf20Sopenharmony_ci		GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", DSMG600_SCL_PIN,
808c2ecf20Sopenharmony_ci				NULL, 1, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
818c2ecf20Sopenharmony_ci	},
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic struct platform_device dsmg600_i2c_gpio = {
858c2ecf20Sopenharmony_ci	.name			= "i2c-gpio",
868c2ecf20Sopenharmony_ci	.id			= 0,
878c2ecf20Sopenharmony_ci	.dev	 = {
888c2ecf20Sopenharmony_ci		.platform_data	= NULL,
898c2ecf20Sopenharmony_ci	},
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct i2c_board_info __initdata dsmg600_i2c_board_info [] = {
938c2ecf20Sopenharmony_ci	{
948c2ecf20Sopenharmony_ci		I2C_BOARD_INFO("pcf8563", 0x51),
958c2ecf20Sopenharmony_ci	},
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic struct gpio_led dsmg600_led_pins[] = {
998c2ecf20Sopenharmony_ci	{
1008c2ecf20Sopenharmony_ci		.name		= "dsmg600:green:power",
1018c2ecf20Sopenharmony_ci		.gpio		= DSMG600_LED_PWR_GPIO,
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci	{
1048c2ecf20Sopenharmony_ci		.name		= "dsmg600:green:wlan",
1058c2ecf20Sopenharmony_ci		.gpio		= DSMG600_LED_WLAN_GPIO,
1068c2ecf20Sopenharmony_ci		.active_low	= true,
1078c2ecf20Sopenharmony_ci	},
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic struct gpio_led_platform_data dsmg600_led_data = {
1118c2ecf20Sopenharmony_ci	.num_leds		= ARRAY_SIZE(dsmg600_led_pins),
1128c2ecf20Sopenharmony_ci	.leds			= dsmg600_led_pins,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic struct platform_device dsmg600_leds = {
1168c2ecf20Sopenharmony_ci	.name			= "leds-gpio",
1178c2ecf20Sopenharmony_ci	.id			= -1,
1188c2ecf20Sopenharmony_ci	.dev.platform_data	= &dsmg600_led_data,
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic struct resource dsmg600_uart_resources[] = {
1228c2ecf20Sopenharmony_ci	{
1238c2ecf20Sopenharmony_ci		.start		= IXP4XX_UART1_BASE_PHYS,
1248c2ecf20Sopenharmony_ci		.end		= IXP4XX_UART1_BASE_PHYS + 0x0fff,
1258c2ecf20Sopenharmony_ci		.flags		= IORESOURCE_MEM,
1268c2ecf20Sopenharmony_ci	},
1278c2ecf20Sopenharmony_ci	{
1288c2ecf20Sopenharmony_ci		.start		= IXP4XX_UART2_BASE_PHYS,
1298c2ecf20Sopenharmony_ci		.end		= IXP4XX_UART2_BASE_PHYS + 0x0fff,
1308c2ecf20Sopenharmony_ci		.flags		= IORESOURCE_MEM,
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic struct plat_serial8250_port dsmg600_uart_data[] = {
1358c2ecf20Sopenharmony_ci	{
1368c2ecf20Sopenharmony_ci		.mapbase	= IXP4XX_UART1_BASE_PHYS,
1378c2ecf20Sopenharmony_ci		.membase	= (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET,
1388c2ecf20Sopenharmony_ci		.irq		= IRQ_IXP4XX_UART1,
1398c2ecf20Sopenharmony_ci		.flags		= UPF_BOOT_AUTOCONF,
1408c2ecf20Sopenharmony_ci		.iotype		= UPIO_MEM,
1418c2ecf20Sopenharmony_ci		.regshift	= 2,
1428c2ecf20Sopenharmony_ci		.uartclk	= IXP4XX_UART_XTAL,
1438c2ecf20Sopenharmony_ci	},
1448c2ecf20Sopenharmony_ci	{
1458c2ecf20Sopenharmony_ci		.mapbase	= IXP4XX_UART2_BASE_PHYS,
1468c2ecf20Sopenharmony_ci		.membase	= (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
1478c2ecf20Sopenharmony_ci		.irq		= IRQ_IXP4XX_UART2,
1488c2ecf20Sopenharmony_ci		.flags		= UPF_BOOT_AUTOCONF,
1498c2ecf20Sopenharmony_ci		.iotype		= UPIO_MEM,
1508c2ecf20Sopenharmony_ci		.regshift	= 2,
1518c2ecf20Sopenharmony_ci		.uartclk	= IXP4XX_UART_XTAL,
1528c2ecf20Sopenharmony_ci	},
1538c2ecf20Sopenharmony_ci	{ }
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic struct platform_device dsmg600_uart = {
1578c2ecf20Sopenharmony_ci	.name			= "serial8250",
1588c2ecf20Sopenharmony_ci	.id			= PLAT8250_DEV_PLATFORM,
1598c2ecf20Sopenharmony_ci	.dev.platform_data	= dsmg600_uart_data,
1608c2ecf20Sopenharmony_ci	.num_resources		= ARRAY_SIZE(dsmg600_uart_resources),
1618c2ecf20Sopenharmony_ci	.resource		= dsmg600_uart_resources,
1628c2ecf20Sopenharmony_ci};
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic struct platform_device *dsmg600_devices[] __initdata = {
1658c2ecf20Sopenharmony_ci	&dsmg600_i2c_gpio,
1668c2ecf20Sopenharmony_ci	&dsmg600_flash,
1678c2ecf20Sopenharmony_ci	&dsmg600_leds,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void dsmg600_power_off(void)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	/* enable the pwr cntl and drive it high */
1738c2ecf20Sopenharmony_ci	gpio_direction_output(DSMG600_PO_GPIO, 1);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* This is used to make sure the power-button pusher is serious.  The button
1778c2ecf20Sopenharmony_ci * must be held until the value of this counter reaches zero.
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_cistatic int power_button_countdown;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* Must hold the button down for at least this many counts to be processed */
1828c2ecf20Sopenharmony_ci#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void dsmg600_power_handler(struct timer_list *unused);
1858c2ecf20Sopenharmony_cistatic DEFINE_TIMER(dsmg600_power_timer, dsmg600_power_handler);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic void dsmg600_power_handler(struct timer_list *unused)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	/* This routine is called twice per second to check the
1908c2ecf20Sopenharmony_ci	 * state of the power button.
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (gpio_get_value(DSMG600_PB_GPIO)) {
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		/* IO Pin is 1 (button pushed) */
1968c2ecf20Sopenharmony_ci		if (power_button_countdown > 0)
1978c2ecf20Sopenharmony_ci			power_button_countdown--;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	} else {
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		/* Done on button release, to allow for auto-power-on mods. */
2028c2ecf20Sopenharmony_ci		if (power_button_countdown == 0) {
2038c2ecf20Sopenharmony_ci			/* Signal init to do the ctrlaltdel action,
2048c2ecf20Sopenharmony_ci			 * this will bypass init if it hasn't started
2058c2ecf20Sopenharmony_ci			 * and do a kernel_restart.
2068c2ecf20Sopenharmony_ci			 */
2078c2ecf20Sopenharmony_ci			ctrl_alt_del();
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci			/* Change the state of the power LED to "blink" */
2108c2ecf20Sopenharmony_ci			gpio_set_value(DSMG600_LED_PWR_GPIO, 0);
2118c2ecf20Sopenharmony_ci		} else {
2128c2ecf20Sopenharmony_ci			power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic irqreturn_t dsmg600_reset_handler(int irq, void *dev_id)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	/* This is the paper-clip reset, it shuts the machine down directly. */
2228c2ecf20Sopenharmony_ci	machine_power_off();
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void __init dsmg600_timer_init(void)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci    /* The xtal on this machine is non-standard. */
2308c2ecf20Sopenharmony_ci    ixp4xx_timer_freq = DSMG600_FREQ;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci    /* Call standard timer_init function. */
2338c2ecf20Sopenharmony_ci    ixp4xx_timer_init();
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int __init dsmg600_gpio_init(void)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	if (!machine_is_dsmg600())
2398c2ecf20Sopenharmony_ci		return 0;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	gpio_request(DSMG600_RB_GPIO, "reset button");
2428c2ecf20Sopenharmony_ci	if (request_irq(gpio_to_irq(DSMG600_RB_GPIO), &dsmg600_reset_handler,
2438c2ecf20Sopenharmony_ci		IRQF_TRIGGER_LOW, "DSM-G600 reset button", NULL) < 0) {
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
2468c2ecf20Sopenharmony_ci			gpio_to_irq(DSMG600_RB_GPIO));
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/*
2508c2ecf20Sopenharmony_ci	 * The power button on the D-Link DSM-G600 is on GPIO 15, but
2518c2ecf20Sopenharmony_ci	 * it cannot handle interrupts on that GPIO line.  So we'll
2528c2ecf20Sopenharmony_ci	 * have to poll it with a kernel timer.
2538c2ecf20Sopenharmony_ci	 */
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* Make sure that the power button GPIO is set up as an input */
2568c2ecf20Sopenharmony_ci	gpio_request(DSMG600_PB_GPIO, "power button");
2578c2ecf20Sopenharmony_ci	gpio_direction_input(DSMG600_PB_GPIO);
2588c2ecf20Sopenharmony_ci	/* Request poweroff GPIO line */
2598c2ecf20Sopenharmony_ci	gpio_request(DSMG600_PO_GPIO, "power off button");
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* Set the initial value for the power button IRQ handler */
2628c2ecf20Sopenharmony_ci	power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_cidevice_initcall(dsmg600_gpio_init);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic void __init dsmg600_init(void)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	ixp4xx_sys_init();
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	dsmg600_flash_resource.start = IXP4XX_EXP_BUS_BASE(0);
2748c2ecf20Sopenharmony_ci	dsmg600_flash_resource.end =
2758c2ecf20Sopenharmony_ci		IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	gpiod_add_lookup_table(&dsmg600_i2c_gpiod_table);
2788c2ecf20Sopenharmony_ci	i2c_register_board_info(0, dsmg600_i2c_board_info,
2798c2ecf20Sopenharmony_ci				ARRAY_SIZE(dsmg600_i2c_board_info));
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* The UART is required on the DSM-G600 (Redboot cannot use the
2828c2ecf20Sopenharmony_ci	 * NIC) -- do it here so that it does *not* get removed if
2838c2ecf20Sopenharmony_ci	 * platform_add_devices fails!
2848c2ecf20Sopenharmony_ci         */
2858c2ecf20Sopenharmony_ci        (void)platform_device_register(&dsmg600_uart);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	platform_add_devices(dsmg600_devices, ARRAY_SIZE(dsmg600_devices));
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	pm_power_off = dsmg600_power_off;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ciMACHINE_START(DSMG600, "D-Link DSM-G600 RevA")
2938c2ecf20Sopenharmony_ci	/* Maintainer: www.nslu2-linux.org */
2948c2ecf20Sopenharmony_ci	.atag_offset	= 0x100,
2958c2ecf20Sopenharmony_ci	.map_io		= ixp4xx_map_io,
2968c2ecf20Sopenharmony_ci	.init_early	= ixp4xx_init_early,
2978c2ecf20Sopenharmony_ci	.init_irq	= ixp4xx_init_irq,
2988c2ecf20Sopenharmony_ci	.init_time	= dsmg600_timer_init,
2998c2ecf20Sopenharmony_ci	.init_machine	= dsmg600_init,
3008c2ecf20Sopenharmony_ci#if defined(CONFIG_PCI)
3018c2ecf20Sopenharmony_ci	.dma_zone_size	= SZ_64M,
3028c2ecf20Sopenharmony_ci#endif
3038c2ecf20Sopenharmony_ci	.restart	= ixp4xx_restart,
3048c2ecf20Sopenharmony_ciMACHINE_END
305