1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

[lantiq] cleanup patches

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@32953 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
blogic
2012-08-03 08:53:02 +00:00
parent 6b899d5dea
commit cea2b4210d
209 changed files with 146978 additions and 82080 deletions

View File

@@ -0,0 +1,11 @@
if SOC_FALCON
menu "MIPS Machine"
config LANTIQ_MACH_EASY98000
bool "Easy98000"
default y
endmenu
endif

View File

@@ -0,0 +1,2 @@
obj-y := prom.o reset.o sysctrl.o devices.o gpio.o
obj-$(CONFIG_LANTIQ_MACH_EASY98000) += mach-easy98000.o

View File

@@ -0,0 +1,152 @@
/*
* 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) 2011 Thomas Langer <thomas.langer@lantiq.com>
* Copyright (C) 2011 John Crispin <blogic@openwrt.org>
*/
#include <linux/platform_device.h>
#include <linux/mtd/nand.h>
#include <linux/gpio.h>
#include <lantiq_soc.h>
#include "devices.h"
/* nand flash */
/* address lines used for NAND control signals */
#define NAND_ADDR_ALE 0x10000
#define NAND_ADDR_CLE 0x20000
/* Ready/Busy Status */
#define MODCON_STS 0x0002
/* Ready/Busy Status Edge */
#define MODCON_STSEDGE 0x0004
static const char *part_probes[] = { "cmdlinepart", NULL };
static int
falcon_nand_ready(struct mtd_info *mtd)
{
u32 modcon = ltq_ebu_r32(LTQ_EBU_MODCON);
return (((modcon & (MODCON_STS | MODCON_STSEDGE)) ==
(MODCON_STS | MODCON_STSEDGE)));
}
static void
falcon_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *this = mtd->priv;
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
if (ctrl & NAND_CTRL_CHANGE) {
nandaddr &= ~(NAND_ADDR_ALE | NAND_ADDR_CLE);
if (ctrl & NAND_CLE)
nandaddr |= NAND_ADDR_CLE;
if (ctrl & NAND_ALE)
nandaddr |= NAND_ADDR_ALE;
this->IO_ADDR_W = (void __iomem *) nandaddr;
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
}
static struct platform_nand_data falcon_flash_nand_data = {
.chip = {
.nr_chips = 1,
.chip_delay = 25,
.part_probe_types = part_probes,
},
.ctrl = {
.cmd_ctrl = falcon_hwcontrol,
.dev_ready = falcon_nand_ready,
}
};
static struct resource ltq_nand_res =
MEM_RES("nand", LTQ_FLASH_START, LTQ_FLASH_MAX);
static struct platform_device ltq_flash_nand = {
.name = "gen_nand",
.id = -1,
.num_resources = 1,
.resource = &ltq_nand_res,
.dev = {
.platform_data = &falcon_flash_nand_data,
},
};
void __init
falcon_register_nand(void)
{
platform_device_register(&ltq_flash_nand);
}
/* gpio */
#define DECLARE_GPIO_RES(port) \
static struct resource falcon_gpio ## port ## _res[] = { \
MEM_RES("gpio"#port, LTQ_GPIO ## port ## _BASE_ADDR, \
LTQ_GPIO ## port ## _SIZE), \
MEM_RES("padctrl"#port, LTQ_PADCTRL ## port ## _BASE_ADDR, \
LTQ_PADCTRL ## port ## _SIZE), \
IRQ_RES("gpio_mux"#port, FALCON_IRQ_GPIO_P ## port) \
}
DECLARE_GPIO_RES(0);
DECLARE_GPIO_RES(1);
DECLARE_GPIO_RES(2);
DECLARE_GPIO_RES(3);
DECLARE_GPIO_RES(4);
void __init
falcon_register_gpio(void)
{
platform_device_register_simple("falcon_gpio", 0,
falcon_gpio0_res, ARRAY_SIZE(falcon_gpio0_res));
platform_device_register_simple("falcon_gpio", 1,
falcon_gpio1_res, ARRAY_SIZE(falcon_gpio1_res));
platform_device_register_simple("falcon_gpio", 2,
falcon_gpio2_res, ARRAY_SIZE(falcon_gpio2_res));
}
void __init
falcon_register_gpio_extra(void)
{
platform_device_register_simple("falcon_gpio", 3,
falcon_gpio3_res, ARRAY_SIZE(falcon_gpio3_res));
platform_device_register_simple("falcon_gpio", 4,
falcon_gpio4_res, ARRAY_SIZE(falcon_gpio4_res));
}
/* spi flash */
static struct platform_device ltq_spi = {
.name = "falcon_spi",
.num_resources = 0,
};
void __init
falcon_register_spi_flash(struct spi_board_info *data)
{
spi_register_board_info(data, 1);
platform_device_register(&ltq_spi);
}
/* i2c */
static struct resource falcon_i2c_resources[] = {
MEM_RES("i2c", GPON_I2C_BASE, GPON_I2C_SIZE),
IRQ_RES(i2c_lb, FALCON_IRQ_I2C_LBREQ),
IRQ_RES(i2c_b, FALCON_IRQ_I2C_BREQ),
IRQ_RES(i2c_err, FALCON_IRQ_I2C_I2C_ERR),
IRQ_RES(i2c_p, FALCON_IRQ_I2C_I2C_P),
};
void __init
falcon_register_i2c(void)
{
platform_device_register_simple("i2c-falcon", 0,
falcon_i2c_resources, ARRAY_SIZE(falcon_i2c_resources));
}

View File

@@ -0,0 +1,25 @@
/*
* 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.
*
* Copyright (C) 2011 Thomas Langer <thomas.langer@lantiq.com>
* Copyright (C) 2011 John Crispin <blogic@openwrt.org>
*/
#ifndef _FALCON_DEVICES_H__
#define _FALCON_DEVICES_H__
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include "../devices.h"
extern void falcon_register_nand(void);
extern void falcon_register_gpio(void);
extern void falcon_register_gpio_extra(void);
extern void falcon_register_spi_flash(struct spi_board_info *data);
extern void falcon_register_i2c(void);
#endif

View File

@@ -0,0 +1,409 @@
/*
* 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) 2011 Thomas Langer <thomas.langer@lantiq.com>
* Copyright (C) 2011 John Crispin <blogic@openwrt.org>
*/
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <lantiq_soc.h>
/* Multiplexer Control Register */
#define LTQ_PADC_MUX(x) (x * 0x4)
/* Pad Control Availability Register */
#define LTQ_PADC_AVAIL 0x000000F0
/* Data Output Register */
#define LTQ_GPIO_OUT 0x00000000
/* Data Input Register */
#define LTQ_GPIO_IN 0x00000004
/* Direction Register */
#define LTQ_GPIO_DIR 0x00000008
/* External Interrupt Control Register 0 */
#define LTQ_GPIO_EXINTCR0 0x00000018
/* External Interrupt Control Register 1 */
#define LTQ_GPIO_EXINTCR1 0x0000001C
/* IRN Capture Register */
#define LTQ_GPIO_IRNCR 0x00000020
/* IRN Interrupt Configuration Register */
#define LTQ_GPIO_IRNCFG 0x0000002C
/* IRN Interrupt Enable Set Register */
#define LTQ_GPIO_IRNRNSET 0x00000030
/* IRN Interrupt Enable Clear Register */
#define LTQ_GPIO_IRNENCLR 0x00000034
/* Output Set Register */
#define LTQ_GPIO_OUTSET 0x00000040
/* Output Cler Register */
#define LTQ_GPIO_OUTCLR 0x00000044
/* Direction Clear Register */
#define LTQ_GPIO_DIRSET 0x00000048
/* Direction Set Register */
#define LTQ_GPIO_DIRCLR 0x0000004C
/* turn a gpio_chip into a falcon_gpio_port */
#define ctop(c) container_of(c, struct falcon_gpio_port, gpio_chip)
/* turn a irq_data into a falcon_gpio_port */
#define itop(i) ((struct falcon_gpio_port *) irq_get_chip_data(i->irq))
#define ltq_pad_r32(p, reg) ltq_r32(p->pad + reg)
#define ltq_pad_w32(p, val, reg) ltq_w32(val, p->pad + reg)
#define ltq_pad_w32_mask(c, clear, set, reg) \
ltq_pad_w32(c, (ltq_pad_r32(c, reg) & ~(clear)) | (set), reg)
#define ltq_port_r32(p, reg) ltq_r32(p->port + reg)
#define ltq_port_w32(p, val, reg) ltq_w32(val, p->port + reg)
#define ltq_port_w32_mask(p, clear, set, reg) \
ltq_port_w32(p, (ltq_port_r32(p, reg) & ~(clear)) | (set), reg)
#define MAX_PORTS 5
#define PINS_PER_PORT 32
struct falcon_gpio_port {
struct gpio_chip gpio_chip;
void __iomem *pad;
void __iomem *port;
unsigned int irq_base;
unsigned int chained_irq;
struct clk *clk;
};
static struct falcon_gpio_port ltq_gpio_port[MAX_PORTS];
int gpio_to_irq(unsigned int gpio)
{
return __gpio_to_irq(gpio);
}
EXPORT_SYMBOL(gpio_to_irq);
int ltq_gpio_mux_set(unsigned int pin, unsigned int mux)
{
int port = pin / 100;
int offset = pin % 100;
struct falcon_gpio_port *gpio_port;
if ((offset >= PINS_PER_PORT) || (port >= MAX_PORTS))
return -EINVAL;
gpio_port = &ltq_gpio_port[port];
ltq_pad_w32(gpio_port, mux & 0x3, LTQ_PADC_MUX(offset));
return 0;
}
EXPORT_SYMBOL(ltq_gpio_mux_set);
int ltq_gpio_request(struct device *dev, unsigned int pin, unsigned int mux,
unsigned int dir, const char *name)
{
int port = pin / 100;
int offset = pin % 100;
if (offset >= PINS_PER_PORT || port >= MAX_PORTS)
return -EINVAL;
if (devm_gpio_request(dev, pin, name)) {
pr_err("failed to setup lantiq gpio: %s\n", name);
return -EBUSY;
}
if (dir)
gpio_direction_output(pin, 1);
else
gpio_direction_input(pin);
return ltq_gpio_mux_set(pin, mux);
}
EXPORT_SYMBOL(ltq_gpio_request);
static int
falcon_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_DIRCLR);
return 0;
}
static void
falcon_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
if (value)
ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_OUTSET);
else
ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_OUTCLR);
}
static int
falcon_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
falcon_gpio_set(chip, offset, value);
ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_DIRSET);
return 0;
}
static int
falcon_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
if ((ltq_port_r32(ctop(chip), LTQ_GPIO_DIR) >> offset) & 1)
return (ltq_port_r32(ctop(chip), LTQ_GPIO_OUT) >> offset) & 1;
else
return (ltq_port_r32(ctop(chip), LTQ_GPIO_IN) >> offset) & 1;
}
static int
falcon_gpio_request(struct gpio_chip *chip, unsigned offset)
{
if ((ltq_pad_r32(ctop(chip), LTQ_PADC_AVAIL) >> offset) & 1) {
if (ltq_pad_r32(ctop(chip), LTQ_PADC_MUX(offset)) > 1)
return -EBUSY;
/* switch on gpio function */
ltq_pad_w32(ctop(chip), 1, LTQ_PADC_MUX(offset));
return 0;
}
return -ENODEV;
}
static void
falcon_gpio_free(struct gpio_chip *chip, unsigned offset)
{
if ((ltq_pad_r32(ctop(chip), LTQ_PADC_AVAIL) >> offset) & 1) {
if (ltq_pad_r32(ctop(chip), LTQ_PADC_MUX(offset)) > 1)
return;
/* switch off gpio function */
ltq_pad_w32(ctop(chip), 0, LTQ_PADC_MUX(offset));
}
}
static int
falcon_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
return ctop(chip)->irq_base + offset;
}
static void
falcon_gpio_disable_irq(struct irq_data *d)
{
unsigned int offset = d->irq - itop(d)->irq_base;
ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNENCLR);
}
static void
falcon_gpio_enable_irq(struct irq_data *d)
{
unsigned int offset = d->irq - itop(d)->irq_base;
if (!ltq_pad_r32(itop(d), LTQ_PADC_MUX(offset)) < 1)
/* switch on gpio function */
ltq_pad_w32(itop(d), 1, LTQ_PADC_MUX(offset));
ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNRNSET);
}
static void
falcon_gpio_ack_irq(struct irq_data *d)
{
unsigned int offset = d->irq - itop(d)->irq_base;
ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNCR);
}
static void
falcon_gpio_mask_and_ack_irq(struct irq_data *d)
{
unsigned int offset = d->irq - itop(d)->irq_base;
ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNENCLR);
ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNCR);
}
static struct irq_chip falcon_gpio_irq_chip;
static int
falcon_gpio_irq_type(struct irq_data *d, unsigned int type)
{
unsigned int offset = d->irq - itop(d)->irq_base;
unsigned int mask = 1 << offset;
if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_NONE)
return 0;
if ((type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) != 0) {
/* level triggered */
ltq_port_w32_mask(itop(d), 0, mask, LTQ_GPIO_IRNCFG);
irq_set_chip_and_handler_name(d->irq,
&falcon_gpio_irq_chip, handle_level_irq, "mux");
} else {
/* edge triggered */
ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_IRNCFG);
irq_set_chip_and_handler_name(d->irq,
&falcon_gpio_irq_chip, handle_simple_irq, "mux");
}
if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_EXINTCR0);
ltq_port_w32_mask(itop(d), 0, mask, LTQ_GPIO_EXINTCR1);
} else {
if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) != 0)
/* positive logic: rising edge, high level */
ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_EXINTCR0);
else
/* negative logic: falling edge, low level */
ltq_port_w32_mask(itop(d), 0, mask, LTQ_GPIO_EXINTCR0);
ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_EXINTCR1);
}
return gpio_direction_input(itop(d)->gpio_chip.base + offset);
}
static void
falcon_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct falcon_gpio_port *gpio_port = irq_desc_get_handler_data(desc);
unsigned long irncr;
int offset;
/* acknowledge interrupt */
irncr = ltq_port_r32(gpio_port, LTQ_GPIO_IRNCR);
ltq_port_w32(gpio_port, irncr, LTQ_GPIO_IRNCR);
desc->irq_data.chip->irq_ack(&desc->irq_data);
for_each_set_bit(offset, &irncr, gpio_port->gpio_chip.ngpio)
generic_handle_irq(gpio_port->irq_base + offset);
}
static struct irq_chip falcon_gpio_irq_chip = {
.name = "gpio_irq_mux",
.irq_mask = falcon_gpio_disable_irq,
.irq_unmask = falcon_gpio_enable_irq,
.irq_ack = falcon_gpio_ack_irq,
.irq_mask_ack = falcon_gpio_mask_and_ack_irq,
.irq_set_type = falcon_gpio_irq_type,
};
static struct irqaction gpio_cascade = {
.handler = no_action,
.flags = IRQF_DISABLED,
.name = "gpio_cascade",
};
static int
falcon_gpio_probe(struct platform_device *pdev)
{
struct falcon_gpio_port *gpio_port;
int ret, i;
struct resource *gpiores, *padres;
int irq;
if (pdev->id >= MAX_PORTS)
return -ENODEV;
gpiores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
padres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
irq = platform_get_irq(pdev, 0);
if (!gpiores || !padres)
return -ENODEV;
gpio_port = &ltq_gpio_port[pdev->id];
gpio_port->gpio_chip.label = "falcon-gpio";
gpio_port->gpio_chip.direction_input = falcon_gpio_direction_input;
gpio_port->gpio_chip.direction_output = falcon_gpio_direction_output;
gpio_port->gpio_chip.get = falcon_gpio_get;
gpio_port->gpio_chip.set = falcon_gpio_set;
gpio_port->gpio_chip.request = falcon_gpio_request;
gpio_port->gpio_chip.free = falcon_gpio_free;
gpio_port->gpio_chip.base = 100 * pdev->id;
gpio_port->gpio_chip.ngpio = 32;
gpio_port->gpio_chip.dev = &pdev->dev;
gpio_port->port = ltq_remap_resource(gpiores);
gpio_port->pad = ltq_remap_resource(padres);
if (!gpio_port->port || !gpio_port->pad) {
dev_err(&pdev->dev, "Could not map io ranges\n");
ret = -ENOMEM;
goto err;
}
gpio_port->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(gpio_port->clk)) {
dev_err(&pdev->dev, "Could not get clock\n");
ret = PTR_ERR(gpio_port->clk);;
goto err;
}
clk_enable(gpio_port->clk);
if (irq > 0) {
/* irq_chip support */
gpio_port->gpio_chip.to_irq = falcon_gpio_to_irq;
gpio_port->irq_base = INT_NUM_EXTRA_START + (32 * pdev->id);
for (i = 0; i < 32; i++) {
irq_set_chip_and_handler_name(gpio_port->irq_base + i,
&falcon_gpio_irq_chip, handle_simple_irq,
"mux");
irq_set_chip_data(gpio_port->irq_base + i, gpio_port);
/* set to negative logic (falling edge, low level) */
ltq_port_w32_mask(gpio_port, 0, 1 << i,
LTQ_GPIO_EXINTCR0);
}
gpio_port->chained_irq = irq;
setup_irq(irq, &gpio_cascade);
irq_set_handler_data(irq, gpio_port);
irq_set_chained_handler(irq, falcon_gpio_irq_handler);
}
ret = gpiochip_add(&gpio_port->gpio_chip);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip %d, %d\n",
pdev->id, ret);
goto err;
}
platform_set_drvdata(pdev, gpio_port);
return ret;
err:
dev_err(&pdev->dev, "Error in gpio_probe %d, %d\n", pdev->id, ret);
if (gpiores)
release_resource(gpiores);
if (padres)
release_resource(padres);
if (gpio_port->port)
iounmap(gpio_port->port);
if (gpio_port->pad)
iounmap(gpio_port->pad);
return ret;
}
static struct platform_driver falcon_gpio_driver = {
.probe = falcon_gpio_probe,
.driver = {
.name = "falcon_gpio",
.owner = THIS_MODULE,
},
};
int __init
falcon_gpio_init(void)
{
int ret;
pr_info("FALC(tm) ON GPIO Driver, (C) 2011 Lantiq Deutschland Gmbh\n");
ret = platform_driver_register(&falcon_gpio_driver);
if (ret)
pr_err("falcon_gpio: Error registering platform driver!");
return ret;
}
postcore_initcall(falcon_gpio_init);

View File

@@ -0,0 +1,138 @@
/*
* 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) 2011 Thomas Langer <thomas.langer@lantiq.com>
* Copyright (C) 2011 John Crispin <blogic@openwrt.org>
*/
#include <linux/platform_device.h>
#include <linux/mtd/partitions.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_gpio.h>
#include <linux/spi/eeprom.h>
#include "../machtypes.h"
#include "devices.h"
static struct mtd_partition easy98000_nor_partitions[] = {
{
.name = "uboot",
.offset = 0x0,
.size = 0x40000,
},
{
.name = "uboot_env",
.offset = 0x40000,
.size = 0x40000, /* 2 sectors for redundant env. */
},
{
.name = "linux",
.offset = 0x80000,
.size = 0xF80000, /* map only 16 MiB */
},
};
struct physmap_flash_data easy98000_nor_flash_data = {
.nr_parts = ARRAY_SIZE(easy98000_nor_partitions),
.parts = easy98000_nor_partitions,
};
static struct flash_platform_data easy98000_spi_flash_platform_data = {
.name = "sflash",
.parts = easy98000_nor_partitions,
.nr_parts = ARRAY_SIZE(easy98000_nor_partitions)
};
static struct spi_board_info easy98000_spi_flash_data __initdata = {
.modalias = "m25p80",
.bus_num = 0,
.chip_select = 0,
.max_speed_hz = 10 * 1000 * 1000,
.mode = SPI_MODE_3,
.platform_data = &easy98000_spi_flash_platform_data
};
/* setup gpio based spi bus/device for access to the eeprom on the board */
#define SPI_GPIO_MRST 102
#define SPI_GPIO_MTSR 103
#define SPI_GPIO_CLK 104
#define SPI_GPIO_CS0 105
#define SPI_GPIO_CS1 106
#define SPI_GPIO_BUS_NUM 1
static struct spi_gpio_platform_data easy98000_spi_gpio_data = {
.sck = SPI_GPIO_CLK,
.mosi = SPI_GPIO_MTSR,
.miso = SPI_GPIO_MRST,
.num_chipselect = 2,
};
static struct platform_device easy98000_spi_gpio_device = {
.name = "spi_gpio",
.id = SPI_GPIO_BUS_NUM,
.dev.platform_data = &easy98000_spi_gpio_data,
};
static struct spi_eeprom at25160n = {
.byte_len = 16 * 1024 / 8,
.name = "at25160n",
.page_size = 32,
.flags = EE_ADDR2,
};
static struct spi_board_info easy98000_spi_gpio_devices __initdata = {
.modalias = "at25",
.bus_num = SPI_GPIO_BUS_NUM,
.max_speed_hz = 1000 * 1000,
.mode = SPI_MODE_3,
.chip_select = 1,
.controller_data = (void *) SPI_GPIO_CS1,
.platform_data = &at25160n,
};
static void __init
easy98000_init_common(void)
{
spi_register_board_info(&easy98000_spi_gpio_devices, 1);
platform_device_register(&easy98000_spi_gpio_device);
falcon_register_i2c();
}
static void __init
easy98000_init(void)
{
easy98000_init_common();
ltq_register_nor(&easy98000_nor_flash_data);
}
static void __init
easy98000sf_init(void)
{
easy98000_init_common();
falcon_register_spi_flash(&easy98000_spi_flash_data);
}
static void __init
easy98000nand_init(void)
{
easy98000_init_common();
falcon_register_nand();
}
MIPS_MACHINE(LANTIQ_MACH_EASY98000,
"EASY98000",
"EASY98000 Eval Board",
easy98000_init);
MIPS_MACHINE(LANTIQ_MACH_EASY98000SF,
"EASY98000SF",
"EASY98000 Eval Board (Serial Flash)",
easy98000sf_init);
MIPS_MACHINE(LANTIQ_MACH_EASY98000NAND,
"EASY98000NAND",
"EASY98000 Eval Board (NAND Flash)",
easy98000nand_init);

View File

@@ -0,0 +1,84 @@
/*
* 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) 2011 Thomas Langer <thomas.langer@lantiq.com>
* Copyright (C) 2011 John Crispin <blogic@openwrt.org>
*/
#include <lantiq_soc.h>
#include "devices.h"
#include "../prom.h"
#define SOC_FALCON "Falcon"
#define SOC_FALCON_D "Falcon-D"
#define SOC_FALCON_V "Falcon-V"
#define SOC_FALCON_M "Falcon-M"
#define PART_SHIFT 12
#define PART_MASK 0x0FFFF000
#define REV_SHIFT 28
#define REV_MASK 0xF0000000
#define SREV_SHIFT 22
#define SREV_MASK 0x03C00000
#define TYPE_SHIFT 26
#define TYPE_MASK 0x3C000000
/* this parameter allows us enable/disable asc1 via commandline */
static int register_asc1;
static int __init
ltq_parse_asc1(char *p)
{
register_asc1 = 1;
return 0;
}
__setup("use_asc1", ltq_parse_asc1);
void __init
ltq_soc_setup(void)
{
ltq_register_asc(0);
ltq_register_wdt();
falcon_register_gpio();
if (register_asc1)
ltq_register_asc(1);
}
void __init
ltq_soc_detect(struct ltq_soc_info *i)
{
u32 type;
i->partnum = (ltq_r32(LTQ_FALCON_CHIPID) & PART_MASK) >> PART_SHIFT;
i->rev = (ltq_r32(LTQ_FALCON_CHIPID) & REV_MASK) >> REV_SHIFT;
i->srev = ((ltq_r32(LTQ_FALCON_CHIPCONF) & SREV_MASK) >> SREV_SHIFT);
sprintf(i->rev_type, "%c%d%d", (i->srev & 0x4) ? ('B') : ('A'),
i->rev & 0x7, (i->srev & 0x3) + 1);
switch (i->partnum) {
case SOC_ID_FALCON:
type = (ltq_r32(LTQ_FALCON_CHIPTYPE) & TYPE_MASK) >> TYPE_SHIFT;
switch (type) {
case 0:
i->name = SOC_FALCON_D;
break;
case 1:
i->name = SOC_FALCON_V;
break;
case 2:
i->name = SOC_FALCON_M;
break;
default:
i->name = SOC_FALCON;
break;
}
i->type = SOC_TYPE_FALCON;
break;
default:
unreachable();
break;
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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) 2011 Thomas Langer <thomas.langer@lantiq.com>
* Copyright (C) 2011 John Crispin <blogic@openwrt.org>
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/pm.h>
#include <asm/reboot.h>
#include <linux/export.h>
#include <lantiq_soc.h>
/* CPU0 Reset Source Register */
#define LTQ_SYS1_CPU0RS 0x0040
/* reset cause mask */
#define LTQ_CPU0RS_MASK 0x0003
int
ltq_reset_cause(void)
{
return ltq_sys1_r32(LTQ_SYS1_CPU0RS) & LTQ_CPU0RS_MASK;
}
EXPORT_SYMBOL_GPL(ltq_reset_cause);
#define BOOT_REG_BASE (KSEG1 | 0x1F200000)
#define BOOT_PW1_REG (BOOT_REG_BASE | 0x20)
#define BOOT_PW2_REG (BOOT_REG_BASE | 0x24)
#define BOOT_PW1 0x4C545100
#define BOOT_PW2 0x0051544C
#define WDT_REG_BASE (KSEG1 | 0x1F8803F0)
#define WDT_PW1 0x00BE0000
#define WDT_PW2 0x00DC0000
static void
ltq_machine_restart(char *command)
{
pr_notice("System restart\n");
local_irq_disable();
/* reboot magic */
ltq_w32(BOOT_PW1, (void *)BOOT_PW1_REG); /* 'LTQ\0' */
ltq_w32(BOOT_PW2, (void *)BOOT_PW2_REG); /* '\0QTL' */
ltq_w32(0, (void *)BOOT_REG_BASE); /* reset Bootreg RVEC */
/* watchdog magic */
ltq_w32(WDT_PW1, (void *)WDT_REG_BASE);
ltq_w32(WDT_PW2 |
(0x3 << 26) | /* PWL */
(0x2 << 24) | /* CLKDIV */
(0x1 << 31) | /* enable */
(1), /* reload */
(void *)WDT_REG_BASE);
unreachable();
}
static void
ltq_machine_halt(void)
{
pr_notice("System halted.\n");
local_irq_disable();
unreachable();
}
static void
ltq_machine_power_off(void)
{
pr_notice("Please turn off the power now.\n");
local_irq_disable();
unreachable();
}
static int __init
mips_reboot_setup(void)
{
_machine_restart = ltq_machine_restart;
_machine_halt = ltq_machine_halt;
pm_power_off = ltq_machine_power_off;
return 0;
}
arch_initcall(mips_reboot_setup);

View File

@@ -0,0 +1,211 @@
/*
* 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) 2011 Thomas Langer <thomas.langer@lantiq.com>
* Copyright (C) 2011 John Crispin <blogic@openwrt.org>
*/
#include <linux/ioport.h>
#include <linux/export.h>
#include <linux/clkdev.h>
#include <asm/delay.h>
#include <lantiq_soc.h>
#include "devices.h"
#include "../clk.h"
/* infrastructure control register */
#define SYS1_INFRAC 0x00bc
/* Configuration fuses for drivers and pll */
#define STATUS_CONFIG 0x0040
/* GPE frequency selection */
#define GPPC_OFFSET 24
#define GPEFREQ_MASK 0x00000C0
#define GPEFREQ_OFFSET 10
/* Clock status register */
#define LTQ_SYSCTL_CLKS 0x0000
/* Clock enable register */
#define LTQ_SYSCTL_CLKEN 0x0004
/* Clock clear register */
#define LTQ_SYSCTL_CLKCLR 0x0008
/* Activation Status Register */
#define LTQ_SYSCTL_ACTS 0x0020
/* Activation Register */
#define LTQ_SYSCTL_ACT 0x0024
/* Deactivation Register */
#define LTQ_SYSCTL_DEACT 0x0028
/* reboot Register */
#define LTQ_SYSCTL_RBT 0x002c
/* CPU0 Clock Control Register */
#define LTQ_SYS1_CPU0CC 0x0040
/* clock divider bit */
#define LTQ_CPU0CC_CPUDIV 0x0001
static struct resource ltq_sysctl_res[] = {
MEM_RES("sys1", LTQ_SYS1_BASE_ADDR, LTQ_SYS1_SIZE),
MEM_RES("syseth", LTQ_SYS_ETH_BASE_ADDR, LTQ_SYS_ETH_SIZE),
MEM_RES("sysgpe", LTQ_SYS_GPE_BASE_ADDR, LTQ_SYS_GPE_SIZE),
};
static struct resource ltq_status_res =
MEM_RES("status", LTQ_STATUS_BASE_ADDR, LTQ_STATUS_SIZE);
static struct resource ltq_ebu_res =
MEM_RES("ebu", LTQ_EBU_BASE_ADDR, LTQ_EBU_SIZE);
static void __iomem *ltq_sysctl[3];
static void __iomem *ltq_status_membase;
void __iomem *ltq_sys1_membase;
void __iomem *ltq_ebu_membase;
#define ltq_reg_w32(m, x, y) ltq_w32((x), ltq_sysctl[m] + (y))
#define ltq_reg_r32(m, x) ltq_r32(ltq_sysctl[m] + (x))
#define ltq_reg_w32_mask(m, clear, set, reg) \
ltq_reg_w32(m, (ltq_reg_r32(m, reg) & ~(clear)) | (set), reg)
#define ltq_status_w32(x, y) ltq_w32((x), ltq_status_membase + (y))
#define ltq_status_r32(x) ltq_r32(ltq_status_membase + (x))
static inline void
ltq_sysctl_wait(struct clk *clk,
unsigned int test, unsigned int reg)
{
int err = 1000000;
do {} while (--err && ((ltq_reg_r32(clk->module, reg)
& clk->bits) != test));
if (!err)
pr_err("module de/activation failed %d %08X %08X %08X\n",
clk->module, clk->bits, test,
ltq_reg_r32(clk->module, reg) & clk->bits);
}
static int
ltq_sysctl_activate(struct clk *clk)
{
ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKEN);
ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_ACT);
ltq_sysctl_wait(clk, clk->bits, LTQ_SYSCTL_ACTS);
return 0;
}
static void
ltq_sysctl_deactivate(struct clk *clk)
{
ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKCLR);
ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_DEACT);
ltq_sysctl_wait(clk, 0, LTQ_SYSCTL_ACTS);
}
static int
ltq_sysctl_clken(struct clk *clk)
{
ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKEN);
ltq_sysctl_wait(clk, clk->bits, LTQ_SYSCTL_CLKS);
return 0;
}
static void
ltq_sysctl_clkdis(struct clk *clk)
{
ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKCLR);
ltq_sysctl_wait(clk, 0, LTQ_SYSCTL_CLKS);
}
static void
ltq_sysctl_reboot(struct clk *clk)
{
unsigned int act;
unsigned int bits;
act = ltq_reg_r32(clk->module, LTQ_SYSCTL_ACT);
bits = ~act & clk->bits;
if (bits != 0) {
ltq_reg_w32(clk->module, bits, LTQ_SYSCTL_CLKEN);
ltq_reg_w32(clk->module, bits, LTQ_SYSCTL_ACT);
ltq_sysctl_wait(clk, bits, LTQ_SYSCTL_ACTS);
}
ltq_reg_w32(clk->module, act & clk->bits, LTQ_SYSCTL_RBT);
ltq_sysctl_wait(clk, clk->bits, LTQ_SYSCTL_ACTS);
}
/* enable the ONU core */
static void
ltq_gpe_enable(void)
{
unsigned int freq;
unsigned int status;
/* if if the clock is already enabled */
status = ltq_reg_r32(SYSCTL_SYS1, SYS1_INFRAC);
if (status & (1 << (GPPC_OFFSET + 1)))
return;
if (ltq_status_r32(STATUS_CONFIG) == 0)
freq = 1; /* use 625MHz on unfused chip */
else
freq = (ltq_status_r32(STATUS_CONFIG) &
GPEFREQ_MASK) >>
GPEFREQ_OFFSET;
/* apply new frequency */
ltq_reg_w32_mask(SYSCTL_SYS1, 7 << (GPPC_OFFSET + 1),
freq << (GPPC_OFFSET + 2) , SYS1_INFRAC);
udelay(1);
/* enable new frequency */
ltq_reg_w32_mask(SYSCTL_SYS1, 0, 1 << (GPPC_OFFSET + 1), SYS1_INFRAC);
udelay(1);
}
static inline void
clkdev_add_sys(const char *dev, unsigned int module,
unsigned int bits)
{
struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
clk->cl.dev_id = dev;
clk->cl.con_id = NULL;
clk->cl.clk = clk;
clk->module = module;
clk->bits = bits;
clk->activate = ltq_sysctl_activate;
clk->deactivate = ltq_sysctl_deactivate;
clk->enable = ltq_sysctl_clken;
clk->disable = ltq_sysctl_clkdis;
clk->reboot = ltq_sysctl_reboot;
clkdev_add(&clk->cl);
}
void __init
ltq_soc_init(void)
{
int i;
for (i = 0; i < 3; i++)
ltq_sysctl[i] = ltq_remap_resource(&ltq_sysctl_res[i]);
ltq_sys1_membase = ltq_sysctl[0];
ltq_status_membase = ltq_remap_resource(&ltq_status_res);
ltq_ebu_membase = ltq_remap_resource(&ltq_ebu_res);
ltq_gpe_enable();
/* get our 3 static rates for cpu, fpi and io clocks */
if (ltq_sys1_r32(LTQ_SYS1_CPU0CC) & LTQ_CPU0CC_CPUDIV)
clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M);
else
clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M);
/* add our clock domains */
clkdev_add_sys("falcon_gpio.0", SYSCTL_SYSETH, ACTS_PADCTRL0 | ACTS_P0);
clkdev_add_sys("falcon_gpio.1", SYSCTL_SYS1, ACTS_PADCTRL1 | ACTS_P1);
clkdev_add_sys("falcon_gpio.2", SYSCTL_SYSETH, ACTS_PADCTRL2 | ACTS_P2);
clkdev_add_sys("falcon_gpio.3", SYSCTL_SYS1, ACTS_PADCTRL3 | ACTS_P3);
clkdev_add_sys("falcon_gpio.4", SYSCTL_SYS1, ACTS_PADCTRL4 | ACTS_P4);
clkdev_add_sys("ltq_asc.1", SYSCTL_SYS1, ACTS_ASC1_ACT);
clkdev_add_sys("i2c-falcon.0", SYSCTL_SYS1, ACTS_I2C_ACT);
}

View File

@@ -0,0 +1,16 @@
if SOC_SVIP
menu "Mips Machine"
config LANTIQ_MACH_EASY33016
bool "Easy33016"
default y
config LANTIQ_MACH_EASY336
select SYS_SUPPORTS_LITTLE_ENDIAN
bool "Easy336"
default y
endmenu
endif

View File

@@ -0,0 +1,3 @@
obj-y := devices.o prom.o reset.o clk-svip.o gpio.o dma.o switchip_setup.o pms.o mux.o
obj-$(CONFIG_LANTIQ_MACH_EASY33016) += mach-easy33016.o
obj-$(CONFIG_LANTIQ_MACH_EASY336) += mach-easy336.o

View File

@@ -0,0 +1,100 @@
/*
* 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.
*
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/time.h>
#include <asm/irq.h>
#include <asm/div64.h>
#include <lantiq_soc.h>
#include <base_reg.h>
#include <sys0_reg.h>
#include <sys1_reg.h>
#include <status_reg.h>
static struct svip_reg_status *const status =
(struct svip_reg_status *)LTQ_STATUS_BASE;
static struct svip_reg_sys0 *const sys0 = (struct svip_reg_sys0 *)LTQ_SYS0_BASE;
static struct svip_reg_sys1 *const sys1 = (struct svip_reg_sys1 *)LTQ_SYS1_BASE;
unsigned int ltq_svip_io_region_clock(void)
{
return 200000000; /* 200 MHz */
}
EXPORT_SYMBOL(ltq_svip_io_region_clock);
unsigned int ltq_svip_cpu_hz(void)
{
/* Magic BootROM speed location... */
if ((*(u32 *)0x9fc07ff0) == 1)
return *(u32 *)0x9fc07ff4;
if (STATUS_CONFIG_CLK_MODE_GET(status_r32(config)) == 1) {
/* xT16 */
return 393216000;
} else {
switch (SYS0_PLL1CR_PLLDIV_GET(sys0_r32(pll1cr))) {
case 3:
return 475000000;
case 2:
return 450000000;
case 1:
return 425000000;
default:
return 400000000;
}
}
}
EXPORT_SYMBOL(ltq_svip_cpu_hz);
unsigned int ltq_svip_fpi_hz(void)
{
u32 fbs0_div[2] = {4, 8};
u32 div;
div = SYS1_FPICR_FPIDIV_GET(sys1_r32(fpicr));
return ltq_svip_cpu_hz()/fbs0_div[div];
}
EXPORT_SYMBOL(ltq_svip_fpi_hz);
unsigned int ltq_get_ppl_hz(void)
{
/* Magic BootROM speed location... */
if ((*(u32 *)0x9fc07ff0) == 1)
return *(u32 *)0x9fc07ff4;
if (STATUS_CONFIG_CLK_MODE_GET(status_r32(config)) == 1) {
/* xT16 */
return 393216000;
} else {
switch (SYS0_PLL1CR_PLLDIV_GET(sys0_r32(pll1cr))) {
case 3:
return 475000000;
case 2:
return 450000000;
case 1:
return 425000000;
default:
return 400000000;
}
}
}
unsigned int ltq_get_fbs0_hz(void)
{
u32 fbs0_div[2] = {4, 8};
u32 div;
div = SYS1_FPICR_FPIDIV_GET(sys1_r32(fpicr));
return ltq_get_ppl_hz()/fbs0_div[div];
}
EXPORT_SYMBOL(ltq_get_fbs0_hz);

View File

@@ -0,0 +1,385 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/mtd/physmap.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/etherdevice.h>
#include <linux/reboot.h>
#include <linux/time.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/spi/spi.h>
#include <linux/mtd/nand.h>
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <lantiq.h>
#include <base_reg.h>
#include <sys1_reg.h>
#include <sys2_reg.h>
#include <ebu_reg.h>
#include "devices.h"
#include <lantiq_soc.h>
#include <svip_mux.h>
#include <svip_pms.h>
/* ASC */
void __init svip_register_asc(int port)
{
switch (port) {
case 0:
ltq_register_asc(0);
svip_sys1_clk_enable(SYS1_CLKENR_ASC0);
break;
case 1:
ltq_register_asc(1);
svip_sys1_clk_enable(SYS1_CLKENR_ASC1);
break;
default:
break;
};
}
/* Ethernet */
static unsigned char svip_ethaddr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static struct platform_device ltq_mii = {
.name = "ifxmips_mii0",
.dev = {
.platform_data = svip_ethaddr,
},
};
static int __init svip_set_ethaddr(char *str)
{
sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&svip_ethaddr[0], &svip_ethaddr[1], &svip_ethaddr[2],
&svip_ethaddr[3], &svip_ethaddr[4], &svip_ethaddr[5]);
return 0;
}
__setup("ethaddr=", svip_set_ethaddr);
void __init svip_register_eth(void)
{
if (!is_valid_ether_addr(svip_ethaddr))
random_ether_addr(svip_ethaddr);
platform_device_register(&ltq_mii);
svip_sys1_clk_enable(SYS1_CLKENR_ETHSW);
}
/* Virtual Ethernet */
static struct platform_device ltq_ve = {
.name = "ifxmips_svip_ve",
};
void __init svip_register_virtual_eth(void)
{
platform_device_register(&ltq_ve);
}
/* SPI */
static void __init ltq_register_ssc(int bus_num, unsigned long base, int irq_rx,
int irq_tx, int irq_err, int irq_frm)
{
struct resource res[] = {
{
.name = "regs",
.start = base,
.end = base + 0x20 - 1,
.flags = IORESOURCE_MEM,
}, {
.name = "rx",
.start = irq_rx,
.flags = IORESOURCE_IRQ,
}, {
.name = "tx",
.start = irq_tx,
.flags = IORESOURCE_IRQ,
}, {
.name = "err",
.start = irq_err,
.flags = IORESOURCE_IRQ,
}, {
.name = "frm",
.start = irq_frm,
.flags = IORESOURCE_IRQ,
},
};
platform_device_register_simple("ifx_ssc", bus_num, res,
ARRAY_SIZE(res));
}
static struct spi_board_info bdinfo[] __initdata = {
{
.modalias = "xt16",
.mode = SPI_MODE_3,
.irq = INT_NUM_IM5_IRL0 + 28,
.max_speed_hz = 1000000,
.bus_num = 0,
.chip_select = 1,
},
{
.modalias = "xt16",
.mode = SPI_MODE_3,
.irq = INT_NUM_IM5_IRL0 + 19,
.max_speed_hz = 1000000,
.bus_num = 0,
.chip_select = 2,
},
{
.modalias = "loop",
.mode = SPI_MODE_0 | SPI_LOOP,
.irq = -1,
.max_speed_hz = 10000000,
.bus_num = 0,
.chip_select = 3,
},
};
void __init svip_register_spi(void)
{
ltq_register_ssc(0, LTQ_SSC0_BASE, INT_NUM_IM1_IRL0 + 6,
INT_NUM_IM1_IRL0 + 7, INT_NUM_IM1_IRL0 + 8,
INT_NUM_IM1_IRL0 + 9);
ltq_register_ssc(1, LTQ_SSC1_BASE, INT_NUM_IM1_IRL0 + 10,
INT_NUM_IM1_IRL0 + 11, INT_NUM_IM1_IRL0 + 12,
INT_NUM_IM1_IRL0 + 13);
spi_register_board_info(bdinfo, ARRAY_SIZE(bdinfo));
svip_sys1_clk_enable(SYS1_CLKENR_SSC0 | SYS1_CLKENR_SSC1);
}
void __init svip_register_spi_flash(struct spi_board_info *bdinfo)
{
spi_register_board_info(bdinfo, 1);
}
/* GPIO */
static struct platform_device ltq_gpio = {
.name = "ifxmips_gpio",
};
static struct platform_device ltq_gpiodev = {
.name = "GPIODEV",
};
void __init svip_register_gpio(void)
{
platform_device_register(&ltq_gpio);
platform_device_register(&ltq_gpiodev);
}
/* MUX */
static struct ltq_mux_settings ltq_mux_settings;
static struct platform_device ltq_mux = {
.name = "ltq_mux",
.dev = {
.platform_data = &ltq_mux_settings,
}
};
void __init svip_register_mux(const struct ltq_mux_pin mux_p0[LTQ_MUX_P0_PINS],
const struct ltq_mux_pin mux_p1[LTQ_MUX_P1_PINS],
const struct ltq_mux_pin mux_p2[LTQ_MUX_P2_PINS],
const struct ltq_mux_pin mux_p3[LTQ_MUX_P3_PINS],
const struct ltq_mux_pin mux_p4[LTQ_MUX_P4_PINS])
{
ltq_mux_settings.mux_p0 = mux_p0;
ltq_mux_settings.mux_p1 = mux_p1;
ltq_mux_settings.mux_p2 = mux_p2;
ltq_mux_settings.mux_p3 = mux_p3;
ltq_mux_settings.mux_p4 = mux_p4;
if (mux_p0)
svip_sys1_clk_enable(SYS1_CLKENR_PORT0);
if (mux_p1)
svip_sys1_clk_enable(SYS1_CLKENR_PORT1);
if (mux_p2)
svip_sys1_clk_enable(SYS1_CLKENR_PORT2);
if (mux_p3)
svip_sys1_clk_enable(SYS1_CLKENR_PORT3);
if (mux_p4)
svip_sys2_clk_enable(SYS2_CLKENR_PORT4);
platform_device_register(&ltq_mux);
}
/* NAND */
#define NAND_ADDR_REGION_BASE (LTQ_EBU_SEG1_BASE)
#define NAND_CLE_BIT (1 << 3)
#define NAND_ALE_BIT (1 << 2)
static struct svip_reg_ebu *const ebu = (struct svip_reg_ebu *)LTQ_EBU_BASE;
static int svip_nand_probe(struct platform_device *pdev)
{
ebu_w32(LTQ_EBU_ADDR_SEL_0_BASE_VAL(CPHYSADDR(NAND_ADDR_REGION_BASE)
>> 12)
| LTQ_EBU_ADDR_SEL_0_MASK_VAL(15)
| LTQ_EBU_ADDR_SEL_0_MRME_VAL(0)
| LTQ_EBU_ADDR_SEL_0_REGEN_VAL(1),
addr_sel_0);
ebu_w32(LTQ_EBU_CON_0_WRDIS_VAL(0)
| LTQ_EBU_CON_0_ADSWP_VAL(1)
| LTQ_EBU_CON_0_AGEN_VAL(0x00)
| LTQ_EBU_CON_0_SETUP_VAL(1)
| LTQ_EBU_CON_0_WAIT_VAL(0x00)
| LTQ_EBU_CON_0_WINV_VAL(0)
| LTQ_EBU_CON_0_PW_VAL(0x00)
| LTQ_EBU_CON_0_ALEC_VAL(0)
| LTQ_EBU_CON_0_BCGEN_VAL(0x01)
| LTQ_EBU_CON_0_WAITWRC_VAL(1)
| LTQ_EBU_CON_0_WAITRDC_VAL(1)
| LTQ_EBU_CON_0_HOLDC_VAL(1)
| LTQ_EBU_CON_0_RECOVC_VAL(0)
| LTQ_EBU_CON_0_CMULT_VAL(0x01),
con_0);
/*
* ECC disabled
* CLE, ALE and CS are pulse, all other signal are latches based
* CLE and ALE are active high, PRE, WP, SE and CS/CE are active low
* OUT_CS_S is disabled
* NAND mode is disabled
*/
ebu_w32(LTQ_EBU_NAND_CON_ECC_ON_VAL(0)
| LTQ_EBU_NAND_CON_LAT_EN_VAL(0x38)
| LTQ_EBU_NAND_CON_OUT_CS_S_VAL(0)
| LTQ_EBU_NAND_CON_IN_CS_S_VAL(0)
| LTQ_EBU_NAND_CON_PRE_P_VAL(1)
| LTQ_EBU_NAND_CON_WP_P_VAL(1)
| LTQ_EBU_NAND_CON_SE_P_VAL(1)
| LTQ_EBU_NAND_CON_CS_P_VAL(1)
| LTQ_EBU_NAND_CON_CLE_P_VAL(0)
| LTQ_EBU_NAND_CON_ALE_P_VAL(0)
| LTQ_EBU_NAND_CON_CSMUX_E_VAL(0)
| LTQ_EBU_NAND_CON_NANDMODE_VAL(0),
nand_con);
return 0;
}
static void svip_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct nand_chip *this = mtd->priv;
if (ctrl & NAND_CTRL_CHANGE) {
unsigned long adr;
/* Coming here means to change either the enable state or
* the address for controlling ALE or CLE */
/* NAND_NCE: Select the chip by setting nCE to low.
* This is done in CON register */
if (ctrl & NAND_NCE)
ebu_w32_mask(0, LTQ_EBU_NAND_CON_NANDMODE_VAL(1),
nand_con);
else
ebu_w32_mask(LTQ_EBU_NAND_CON_NANDMODE_VAL(1),
0, nand_con);
/* The addressing of CLE or ALE is done via different addresses.
We are now changing the address depending on the given action
SVIPs NAND_CLE_BIT = (1 << 3), NAND_CLE = 0x02
NAND_ALE_BIT = (1 << 2) = NAND_ALE (0x04) */
adr = (unsigned long)this->IO_ADDR_W;
adr &= ~(NAND_CLE_BIT | NAND_ALE_BIT);
adr |= (ctrl & NAND_CLE) << 2 | (ctrl & NAND_ALE);
this->IO_ADDR_W = (void __iomem *)adr;
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
}
static int svip_nand_ready(struct mtd_info *mtd)
{
return (ebu_r32(nand_wait) & 0x01) == 0x01;
}
static inline void svip_nand_wait(void)
{
static const int nops = 150;
int i;
for (i = 0; i < nops; i++)
asm("nop");
}
static void svip_nand_write_buf(struct mtd_info *mtd,
const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++) {
writeb(buf[i], this->IO_ADDR_W);
svip_nand_wait();
}
}
static void svip_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++) {
buf[i] = readb(this->IO_ADDR_R);
svip_nand_wait();
}
}
static const char *part_probes[] = { "cmdlinepart", NULL };
static struct platform_nand_data svip_flash_nand_data = {
.chip = {
.nr_chips = 1,
.part_probe_types = part_probes,
},
.ctrl = {
.probe = svip_nand_probe,
.cmd_ctrl = svip_nand_hwcontrol,
.dev_ready = svip_nand_ready,
.write_buf = svip_nand_write_buf,
.read_buf = svip_nand_read_buf,
}
};
static struct resource svip_nand_resources[] = {
MEM_RES("nand", LTQ_FLASH_START, LTQ_FLASH_MAX),
};
static struct platform_device svip_flash_nand = {
.name = "gen_nand",
.id = -1,
.num_resources = ARRAY_SIZE(svip_nand_resources),
.resource = svip_nand_resources,
.dev = {
.platform_data = &svip_flash_nand_data,
},
};
void __init svip_register_nand(void)
{
platform_device_register(&svip_flash_nand);
}

View File

@@ -0,0 +1,23 @@
#ifndef _SVIP_DEVICES_H__
#define _SVIP_DEVICES_H__
#include <linux/mtd/physmap.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <svip_mux.h>
#include "../devices.h"
extern void __init svip_register_asc(int port);
extern void __init svip_register_eth(void);
extern void __init svip_register_virtual_eth(void);
extern void __init svip_register_spi(void);
extern void __init svip_register_spi_flash(struct spi_board_info *bdinfo);
extern void __init svip_register_gpio(void);
extern void __init svip_register_mux(const struct ltq_mux_pin mux_p0[LTQ_MUX_P0_PINS],
const struct ltq_mux_pin mux_p1[LTQ_MUX_P1_PINS],
const struct ltq_mux_pin mux_p2[LTQ_MUX_P2_PINS],
const struct ltq_mux_pin mux_p3[LTQ_MUX_P3_PINS],
const struct ltq_mux_pin mux_p4[LTQ_MUX_P4_PINS]);
extern void __init svip_register_nand(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,553 @@
/*
* 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) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/kobject.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/platform_device.h>
#include <net/sock.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/semaphore.h>
#include <lantiq_soc.h>
#include <svip_mux.h>
#include <base_reg.h>
#include <port_reg.h>
#define DRV_NAME "ifxmips_gpio"
int gpio_to_irq(unsigned int gpio)
{
return -EINVAL;
}
EXPORT_SYMBOL(gpio_to_irq);
int irq_to_gpio(unsigned int gpio)
{
return -EINVAL;
}
EXPORT_SYMBOL(irq_to_gpio);
struct ltq_port_base {
struct svip_reg_port *base;
u32 pins;
};
/* Base addresses for ports */
static const struct ltq_port_base ltq_port_base[] = {
{ (struct svip_reg_port *)LTQ_PORT_P0_BASE, 20 },
{ (struct svip_reg_port *)LTQ_PORT_P1_BASE, 20 },
{ (struct svip_reg_port *)LTQ_PORT_P2_BASE, 19 },
{ (struct svip_reg_port *)LTQ_PORT_P3_BASE, 20 },
{ (struct svip_reg_port *)LTQ_PORT_P4_BASE, 24 }
};
#define MAX_PORTS ARRAY_SIZE(ltq_port_base)
#define PINS_PER_PORT(port) (ltq_port_base[port].pins)
static inline
void ltq_port_set_exintcr0(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->exintcr0) | (1 << pin),
ltq_port_base[port].base->exintcr0);
}
static inline
void ltq_port_clear_exintcr0(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->exintcr0) & ~(1 << pin),
ltq_port_base[port].base->exintcr0);
}
static inline
void ltq_port_set_exintcr1(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->exintcr1) | (1 << pin),
ltq_port_base[port].base->exintcr1);
}
static inline
void ltq_port_clear_exintcr1(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->exintcr1) & ~(1 << pin),
ltq_port_base[port].base->exintcr1);
}
static inline
void ltq_port_set_irncfg(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->irncfg) | (1 << pin),
ltq_port_base[port].base->irncfg);
}
static inline
void ltq_port_clear_irncfg(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->irncfg) & ~(1 << pin),
ltq_port_base[port].base->irncfg);
}
static inline
void ltq_port_set_irnen(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(1 << pin, ltq_port_base[port].base->irnenset);
}
static inline
void ltq_port_clear_irnen(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(1 << pin, ltq_port_base[port].base->irnenclr);
}
static inline
void ltq_port_set_dir_out(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->dir) | (1 << pin),
ltq_port_base[port].base->dir);
}
static inline
void ltq_port_set_dir_in(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->dir) & ~(1 << pin),
ltq_port_base[port].base->dir);
}
static inline
void ltq_port_set_output(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->out) | (1 << pin),
ltq_port_base[port].base->out);
}
static inline
void ltq_port_clear_output(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->out) & ~(1 << pin),
ltq_port_base[port].base->out);
}
static inline
int ltq_port_get_input(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return -EINVAL;
return (port_r32(ltq_port_base[port].base->in) & (1 << pin)) == 0;
}
static inline
void ltq_port_set_puen(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->puen) | (1 << pin),
ltq_port_base[port].base->puen);
}
static inline
void ltq_port_clear_puen(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->puen) & ~(1 << pin),
ltq_port_base[port].base->puen);
}
static inline
void ltq_port_set_altsel0(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->altsel0) | (1 << pin),
ltq_port_base[port].base->altsel0);
}
static inline
void ltq_port_clear_altsel0(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->altsel0) & ~(1 << pin),
ltq_port_base[port].base->altsel0);
}
static inline
void ltq_port_set_altsel1(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->altsel1) | (1 << pin),
ltq_port_base[port].base->altsel1);
}
static inline
void ltq_port_clear_altsel1(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return;
port_w32(port_r32(ltq_port_base[port].base->altsel1) & ~(1 << pin),
ltq_port_base[port].base->altsel1);
}
void ltq_gpio_configure(int port, int pin, bool dirin, bool puen,
bool altsel0, bool altsel1)
{
if (dirin)
ltq_port_set_dir_in(port, pin);
else
ltq_port_set_dir_out(port, pin);
if (puen)
ltq_port_set_puen(port, pin);
else
ltq_port_clear_puen(port, pin);
if (altsel0)
ltq_port_set_altsel0(port, pin);
else
ltq_port_clear_altsel0(port, pin);
if (altsel1)
ltq_port_set_altsel1(port, pin);
else
ltq_port_clear_altsel1(port, pin);
}
int ltq_port_get_dir(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return -EINVAL;
return (port_r32(ltq_port_base[port].base->dir) & (1 << pin)) != 0;
}
int ltq_port_get_puden(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return -EINVAL;
return (port_r32(ltq_port_base[port].base->puen) & (1 << pin)) != 0;
}
int ltq_port_get_altsel0(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return -EINVAL;
return (port_r32(ltq_port_base[port].base->altsel0) & (1 << pin)) != 0;
}
int ltq_port_get_altsel1(unsigned int port, unsigned int pin)
{
if (port >= MAX_PORTS || pin >= PINS_PER_PORT(port))
return -EINVAL;
return (port_r32(ltq_port_base[port].base->altsel1) & (1 << pin)) != 0;
}
struct ltq_gpio_port {
struct gpio_chip gpio_chip;
unsigned int irq_base;
unsigned int chained_irq;
};
static struct ltq_gpio_port ltq_gpio_port[MAX_PORTS];
static int gpio_exported;
static int __init gpio_export_setup(char *str)
{
get_option(&str, &gpio_exported);
return 1;
}
__setup("gpio_exported=", gpio_export_setup);
static inline unsigned int offset2port(unsigned int offset)
{
unsigned int i;
unsigned int prev = 0;
for (i = 0; i < ARRAY_SIZE(ltq_port_base); i++) {
if (offset >= prev &&
offset < prev + ltq_port_base[i].pins)
return i;
prev = ltq_port_base[i].pins;
}
return 0;
}
static inline unsigned int offset2pin(unsigned int offset)
{
unsigned int i;
unsigned int prev = 0;
for (i = 0; i < ARRAY_SIZE(ltq_port_base); i++) {
if (offset >= prev &&
offset < prev + ltq_port_base[i].pins)
return offset - prev;
prev = ltq_port_base[i].pins;
}
return 0;
}
static int ltq_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
ltq_port_set_dir_in(offset2port(offset), offset2pin(offset));
return 0;
}
static int ltq_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
ltq_port_set_dir_out(offset2port(offset), offset2pin(offset));
return 0;
}
static int ltq_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
return ltq_port_get_input(offset2port(offset), offset2pin(offset));
}
static void ltq_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
if (value)
ltq_port_set_output(offset2port(offset), offset2pin(offset));
else
ltq_port_clear_output(offset2port(offset), offset2pin(offset));
}
static int svip_gpio_request(struct gpio_chip *chip, unsigned offset)
{
return 0;
}
static void ltq_gpio_free(struct gpio_chip *chip, unsigned offset)
{
}
static int ltq_gpio_probe(struct platform_device *pdev)
{
int ret = 0;
struct ltq_gpio_port *gpio_port;
if (pdev->id >= MAX_PORTS)
return -ENODEV;
gpio_port = &ltq_gpio_port[pdev->id];
gpio_port->gpio_chip.label = "ltq-gpio";
gpio_port->gpio_chip.direction_input = ltq_gpio_direction_input;
gpio_port->gpio_chip.direction_output = ltq_gpio_direction_output;
gpio_port->gpio_chip.get = ltq_gpio_get;
gpio_port->gpio_chip.set = ltq_gpio_set;
gpio_port->gpio_chip.request = svip_gpio_request;
gpio_port->gpio_chip.free = ltq_gpio_free;
gpio_port->gpio_chip.base = 100 * pdev->id;
gpio_port->gpio_chip.ngpio = 32;
gpio_port->gpio_chip.dev = &pdev->dev;
gpio_port->gpio_chip.exported = gpio_exported;
ret = gpiochip_add(&gpio_port->gpio_chip);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip %d, %d\n",
pdev->id, ret);
goto err;
}
platform_set_drvdata(pdev, gpio_port);
return 0;
err:
return ret;
}
static int ltq_gpio_remove(struct platform_device *pdev)
{
struct ltq_gpio_port *gpio_port = platform_get_drvdata(pdev);
int ret;
ret = gpiochip_remove(&gpio_port->gpio_chip);
return ret;
}
static struct platform_driver ltq_gpio_driver = {
.probe = ltq_gpio_probe,
.remove = __devexit_p(ltq_gpio_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
int __init ltq_gpio_init(void)
{
int ret = platform_driver_register(&ltq_gpio_driver);
if (ret)
printk(KERN_INFO DRV_NAME
": Error registering platform driver!");
return ret;
}
postcore_initcall(ltq_gpio_init);
/**
* Convert interrupt number to corresponding port/pin pair
* Returns the port/pin pair serving the selected external interrupt;
* needed since mapping not linear.
*
* \param exint External interrupt number
* \param port Pointer for resulting port
* \param pin Pointer for resutling pin
* \return -EINVAL Invalid exint
* \return 0 port/pin updated
* \ingroup API
*/
static int ltq_exint2port(u32 exint, int *port, int *pin)
{
if ((exint >= 0) && (exint <= 10)) {
*port = 0;
*pin = exint + 7;
} else if ((exint >= 11) && (exint <= 14)) {
*port = 1;
*pin = 18 - (exint - 11) ;
} else if (exint == 15) {
*port = 1;
*pin = 19;
} else if (exint == 16) {
*port = 0;
*pin = 19;
} else {
return -EINVAL;
}
return 0;
}
/**
* Enable external interrupt.
* This function enables an external interrupt and sets the given mode.
* valid values for mode are:
* - 0 = Interrupt generation disabled
* - 1 = Interrupt on rising edge
* - 2 = Interrupt on falling edge
* - 3 = Interrupt on rising and falling edge
* - 5 = Interrupt on high level detection
* - 6 = Interrupt on low level detection
*
* \param exint - Number of external interrupt
* \param mode - Trigger mode
* \return 0 on success
* \ingroup API
*/
int ifx_enable_external_int(u32 exint, u32 mode)
{
int port;
int pin;
if ((mode < 0) || (mode > 6))
return -EINVAL;
if (ltq_exint2port(exint, &port, &pin))
return -EINVAL;
ltq_port_clear_exintcr0(port, pin);
ltq_port_clear_exintcr1(port, pin);
ltq_port_clear_irncfg(port, pin);
if (mode & 0x1)
ltq_port_set_exintcr0(port, pin);
if (mode & 0x2)
ltq_port_set_exintcr1(port, pin);
if (mode & 0x4)
ltq_port_set_irncfg(port, pin);
ltq_port_set_irnen(port, pin);
return 0;
}
EXPORT_SYMBOL(ifx_enable_external_int);
/**
* Disable external interrupt.
* This function disables an external interrupt and sets mode to 0x00.
*
* \param exint - Number of external interrupt
* \return 0 on success
* \ingroup API
*/
int ifx_disable_external_int(u32 exint)
{
int port;
int pin;
if (ltq_exint2port(exint, &port, &pin))
return -EINVAL;
ltq_port_clear_irnen(port, pin);
return 0;
}
EXPORT_SYMBOL(ifx_disable_external_int);

View File

@@ -0,0 +1,73 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/gpio_buttons.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include "../machtypes.h"
#include <sys1_reg.h>
#include <sys2_reg.h>
#include <svip_pms.h>
#include "devices.h"
static const struct ltq_mux_pin mux_p0[LTQ_MUX_P0_PINS] = {
LTQ_MUX_P0_0_SSC0_MTSR,
LTQ_MUX_P0_1_SSC0_MRST,
LTQ_MUX_P0_2_SSC0_SCLK,
LTQ_MUX_P0_3_SSC1_MTSR,
LTQ_MUX_P0_4_SSC1_MRST,
LTQ_MUX_P0_5_SSC1_SCLK,
LTQ_MUX_P0_6_SSC0_CS0,
LTQ_MUX_P0_7_SSC0_CS1,
LTQ_MUX_P0_8_SSC0_CS2,
LTQ_MUX_P0_9,
LTQ_MUX_P0_10,
LTQ_MUX_P0_11_EXINT4,
LTQ_MUX_P0_12,
LTQ_MUX_P0_13,
LTQ_MUX_P0_14_ASC0_TXD,
LTQ_MUX_P0_15_ASC0_RXD,
LTQ_MUX_P0_16_EXINT9,
LTQ_MUX_P0_17_EXINT10,
LTQ_MUX_P0_18_EJ_BRKIN,
LTQ_MUX_P0_19_EXINT16
};
static void __init easy33016_init(void)
{
svip_sys1_clk_enable(SYS1_CLKENR_L2C |
SYS1_CLKENR_DDR2 |
SYS1_CLKENR_SMI2 |
SYS1_CLKENR_SMI1 |
SYS1_CLKENR_SMI0 |
SYS1_CLKENR_FMI0 |
SYS1_CLKENR_DMA |
SYS1_CLKENR_SSC0 |
SYS1_CLKENR_SSC1 |
SYS1_CLKENR_EBU);
svip_sys2_clk_enable(SYS2_CLKENR_HWSYNC |
SYS2_CLKENR_MBS |
SYS2_CLKENR_SWINT);
svip_register_mux(mux_p0, NULL, NULL, NULL, NULL);
svip_register_asc(0);
svip_register_eth();
svip_register_virtual_eth();
ltq_register_wdt();
svip_register_gpio();
svip_register_spi();
svip_register_nand();
}
MIPS_MACHINE(LANTIQ_MACH_EASY33016,
"EASY33016",
"EASY33016",
easy33016_init);

View File

@@ -0,0 +1,221 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/gpio_buttons.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include "../machtypes.h"
#include <sys1_reg.h>
#include <sys2_reg.h>
#include <svip_pms.h>
#include "devices.h"
static struct mtd_partition easy336_sflash_partitions[] = {
{
.name = "SPI flash",
.size = MTDPART_SIZ_FULL,
.offset = 0,
},
};
static struct flash_platform_data easy336_sflash_data = {
.name = "m25p32",
.parts = (void *)&easy336_sflash_partitions,
.nr_parts = ARRAY_SIZE(easy336_sflash_partitions),
.type = "m25p32",
};
static struct spi_board_info bdinfo[] __initdata = {
{
.modalias = "m25p80",
.platform_data = &easy336_sflash_data,
.mode = SPI_MODE_0,
.irq = -1,
.max_speed_hz = 25000000,
.bus_num = 0,
.chip_select = 0,
}
};
static struct mtd_partition easy336_partitions[] = {
{
.name = "uboot",
.offset = 0x0,
.size = 0x40000,
},
{
.name = "uboot_env",
.offset = 0x40000,
.size = 0x20000,
},
{
.name = "linux",
.offset = 0x60000,
.size = 0x1a0000,
},
{
.name = "rootfs",
.offset = 0x200000,
.size = 0x500000,
},
};
static struct physmap_flash_data easy336_flash_data = {
.nr_parts = ARRAY_SIZE(easy336_partitions),
.parts = easy336_partitions,
};
static const struct ltq_mux_pin mux_p0[LTQ_MUX_P0_PINS] = {
LTQ_MUX_P0_0_SSC0_MTSR,
LTQ_MUX_P0_1_SSC0_MRST,
LTQ_MUX_P0_2_SSC0_SCLK,
LTQ_MUX_P0_3_SSC1_MTSR,
LTQ_MUX_P0_4_SSC1_MRST,
LTQ_MUX_P0_5_SSC1_SCLK,
LTQ_MUX_P0_6_SSC0_CS0,
LTQ_MUX_P0_7_SSC0_CS1,
LTQ_MUX_P0_8_SSC0_CS2,
LTQ_MUX_P0_9_SSC0_CS3,
LTQ_MUX_P0_10_SSC0_CS4,
LTQ_MUX_P0_11_SSC0_CS5,
LTQ_MUX_P0_12_EXINT5,
LTQ_MUX_P0_13_EXINT6,
LTQ_MUX_P0_14_ASC0_TXD,
LTQ_MUX_P0_15_ASC0_RXD,
LTQ_MUX_P0_16_EXINT9,
LTQ_MUX_P0_17_EXINT10,
LTQ_MUX_P0_18_EJ_BRKIN,
LTQ_MUX_P0_19_EXINT16
};
static const struct ltq_mux_pin mux_p2[LTQ_MUX_P2_PINS] = {
LTQ_MUX_P2_0_EBU_A0,
LTQ_MUX_P2_1_EBU_A1,
LTQ_MUX_P2_2_EBU_A2,
LTQ_MUX_P2_3_EBU_A3,
LTQ_MUX_P2_4_EBU_A4,
LTQ_MUX_P2_5_EBU_A5,
LTQ_MUX_P2_6_EBU_A6,
LTQ_MUX_P2_7_EBU_A7,
LTQ_MUX_P2_8_EBU_A8,
LTQ_MUX_P2_9_EBU_A9,
LTQ_MUX_P2_10_EBU_A10,
LTQ_MUX_P2_11_EBU_A11,
LTQ_MUX_P2_12_EBU_RD,
LTQ_MUX_P2_13_EBU_WR,
LTQ_MUX_P2_14_EBU_ALE,
LTQ_MUX_P2_15_EBU_WAIT,
LTQ_MUX_P2_16_EBU_RDBY,
LTQ_MUX_P2_17_EBU_BC0,
LTQ_MUX_P2_18_EBU_BC1
};
static const struct ltq_mux_pin mux_p3[LTQ_MUX_P3_PINS] = {
LTQ_MUX_P3_0_EBU_AD0,
LTQ_MUX_P3_1_EBU_AD1,
LTQ_MUX_P3_2_EBU_AD2,
LTQ_MUX_P3_3_EBU_AD3,
LTQ_MUX_P3_4_EBU_AD4,
LTQ_MUX_P3_5_EBU_AD5,
LTQ_MUX_P3_6_EBU_AD6,
LTQ_MUX_P3_7_EBU_AD7,
LTQ_MUX_P3_8_EBU_AD8,
LTQ_MUX_P3_9_EBU_AD9,
LTQ_MUX_P3_10_EBU_AD10,
LTQ_MUX_P3_11_EBU_AD11,
LTQ_MUX_P3_12_EBU_AD12,
LTQ_MUX_P3_13_EBU_AD13,
LTQ_MUX_P3_14_EBU_AD14,
LTQ_MUX_P3_15_EBU_AD15,
LTQ_MUX_P3_16_EBU_CS0,
LTQ_MUX_P3_17_EBU_CS1,
LTQ_MUX_P3_18_EBU_CS2,
LTQ_MUX_P3_19_EBU_CS3
};
static void __init easy336_init_common(void)
{
svip_sys1_clk_enable(SYS1_CLKENR_L2C |
SYS1_CLKENR_DDR2 |
SYS1_CLKENR_SMI2 |
SYS1_CLKENR_SMI1 |
SYS1_CLKENR_SMI0 |
SYS1_CLKENR_FMI0 |
SYS1_CLKENR_DMA |
SYS1_CLKENR_GPTC |
SYS1_CLKENR_EBU);
svip_sys2_clk_enable(SYS2_CLKENR_HWSYNC |
SYS2_CLKENR_MBS |
SYS2_CLKENR_SWINT |
SYS2_CLKENR_HWACC3 |
SYS2_CLKENR_HWACC2 |
SYS2_CLKENR_HWACC1 |
SYS2_CLKENR_HWACC0 |
SYS2_CLKENR_SIF7 |
SYS2_CLKENR_SIF6 |
SYS2_CLKENR_SIF5 |
SYS2_CLKENR_SIF4 |
SYS2_CLKENR_SIF3 |
SYS2_CLKENR_SIF2 |
SYS2_CLKENR_SIF1 |
SYS2_CLKENR_SIF0 |
SYS2_CLKENR_DFEV7 |
SYS2_CLKENR_DFEV6 |
SYS2_CLKENR_DFEV5 |
SYS2_CLKENR_DFEV4 |
SYS2_CLKENR_DFEV3 |
SYS2_CLKENR_DFEV2 |
SYS2_CLKENR_DFEV1 |
SYS2_CLKENR_DFEV0);
svip_register_mux(mux_p0, NULL, mux_p2, mux_p3, NULL);
svip_register_asc(0);
svip_register_eth();
svip_register_virtual_eth();
/* ltq_register_wdt(); - conflicts with lq_switch */
svip_register_gpio();
svip_register_spi();
ltq_register_tapi();
}
static void __init easy336_init(void)
{
easy336_init_common();
ltq_register_nor(&easy336_flash_data);
}
static void __init easy336sf_init(void)
{
easy336_init_common();
svip_register_spi_flash(bdinfo);
}
static void __init easy336nand_init(void)
{
easy336_init_common();
svip_register_nand();
}
MIPS_MACHINE(LANTIQ_MACH_EASY336,
"EASY336",
"EASY336",
easy336_init);
MIPS_MACHINE(LANTIQ_MACH_EASY336SF,
"EASY336SF",
"EASY336 (Serial Flash)",
easy336sf_init);
MIPS_MACHINE(LANTIQ_MACH_EASY336NAND,
"EASY336NAND",
"EASY336 (NAND Flash)",
easy336nand_init);

View File

@@ -0,0 +1,187 @@
/************************************************************************
*
* Copyright (c) 2007
* Infineon Technologies AG
* St. Martin Strasse 53; 81669 Muenchen; Germany
*
* 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.
*
************************************************************************/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <asm/addrspace.h>
#include <linux/platform_device.h>
#include <lantiq_soc.h>
#include <svip_mux.h>
#include <sys1_reg.h>
#include <sys2_reg.h>
#include <svip_pms.h>
#define DRV_NAME "ltq_mux"
static void ltq_mux_port_init(const int port,
const struct ltq_mux_pin *pins,
const int pin_max)
{
unsigned int i;
for (i = 0; i < pin_max; i++)
ltq_gpio_configure(port,
i,
pins[i].dirin,
pins[i].puen,
pins[i].altsel0,
pins[i].altsel1);
}
static int ltq_mux_probe(struct platform_device *pdev)
{
struct ltq_mux_settings *mux_settings = dev_get_platdata(&pdev->dev);
if (mux_settings->mux_p0)
ltq_mux_port_init(0,
mux_settings->mux_p0,
LTQ_MUX_P0_PINS);
if (mux_settings->mux_p1)
ltq_mux_port_init(1,
mux_settings->mux_p1,
LTQ_MUX_P1_PINS);
if (mux_settings->mux_p2)
ltq_mux_port_init(2,
mux_settings->mux_p2,
LTQ_MUX_P2_PINS);
if (mux_settings->mux_p3)
ltq_mux_port_init(3,
mux_settings->mux_p3,
LTQ_MUX_P3_PINS);
if (mux_settings->mux_p4)
ltq_mux_port_init(4,
mux_settings->mux_p4,
LTQ_MUX_P4_PINS);
return 0;
}
int ltq_mux_read_procmem(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
int len = 0;
int t = 0, i = 0;
u32 port_clk[5] = {
SYS1_CLKENR_PORT0,
SYS1_CLKENR_PORT1,
SYS1_CLKENR_PORT2,
SYS1_CLKENR_PORT3,
SYS2_CLKENR_PORT4,
};
#define PROC_PRINT(fmt, args...) \
do { \
int c_len = 0; \
c_len = snprintf(buf + len, count - len, fmt, ## args); \
if (c_len <= 0) \
goto out; \
if (c_len >= (count - len)) { \
len += (count - len); \
goto out; \
} \
len += c_len; \
if (offset > 0) { \
if (len > offset) { \
len -= offset; \
memmove(buf, buf + offset, len); \
offset = 0; \
} else { \
offset -= len; \
len = 0; \
} \
} \
} while (0)
PROC_PRINT("\nVINETIC-SVIP Multiplex Settings\n");
PROC_PRINT(" 3 2 1 0\n");
PROC_PRINT(" 10987654321098765432109876543210\n");
PROC_PRINT(" --------------------------------\n");
for (i = 0; i < ARRAY_SIZE(port_clk); i++) {
if (i < 4) {
if (!svip_sys1_clk_is_enabled(port_clk[i]))
continue;
} else {
if (!svip_sys2_clk_is_enabled(port_clk[i]))
continue;
}
PROC_PRINT("P%d.%-10s", i, "DIR:");
for (t = 31; t != -1; t--)
PROC_PRINT("%d", ltq_port_get_dir(i, t) == 1 ? 1 : 0);
PROC_PRINT("\n");
PROC_PRINT("P%d.%-10s", i, "PUEN:");
for (t = 31; t != -1; t--)
PROC_PRINT("%d", ltq_port_get_puden(i, t) == 1 ? 1 : 0);
PROC_PRINT("\n");
PROC_PRINT("P%d.%-10s", i, "ALTSEL0:");
for (t = 31; t != -1; t--)
PROC_PRINT("%d",
ltq_port_get_altsel0(i, t) == 1 ? 1 : 0);
PROC_PRINT("\n");
PROC_PRINT("P%d.%-10s", i, "ALTSEL1:");
for (t = 31; t != -1; t--)
PROC_PRINT("%d",
ltq_port_get_altsel1(i, t) == 1 ? 1 : 0);
PROC_PRINT("\n\n");
}
out:
if (len < 0) {
len = 0;
*eof = 1;
} else if (len < count) {
*eof = 1;
} else {
len = count;
}
*start = buf;
return len;
}
static struct platform_driver ltq_mux_driver = {
.probe = ltq_mux_probe,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
int __init ltq_mux_init(void)
{
int ret = platform_driver_register(&ltq_mux_driver);
if (ret) {
printk(KERN_INFO DRV_NAME
": Error registering platform driver!");
return ret;
}
return create_proc_read_entry("driver/ltq_mux", 0, NULL,
ltq_mux_read_procmem, NULL) == NULL;
}
module_init(ltq_mux_init);

View File

@@ -0,0 +1,101 @@
/************************************************************************
*
* Copyright (c) 2007
* Infineon Technologies AG
* St. Martin Strasse 53; 81669 Muenchen; Germany
*
* 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.
*
************************************************************************/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <asm/addrspace.h>
#include <base_reg.h>
#include <sys1_reg.h>
#include <sys2_reg.h>
#include <lantiq_soc.h>
static struct svip_reg_sys1 *const sys1 = (struct svip_reg_sys1 *)LTQ_SYS1_BASE;
static struct svip_reg_sys2 *const sys2 = (struct svip_reg_sys2 *)LTQ_SYS2_BASE;
void svip_sys1_clk_enable(u32 mask)
{
sys1_w32(sys1_r32(clksr) | mask, clkenr);
asm("sync;");
}
EXPORT_SYMBOL(svip_sys1_clk_enable);
int svip_sys1_clk_is_enabled(u32 mask)
{
return (sys1_r32(clksr) & mask) != 0;
}
EXPORT_SYMBOL(svip_sys1_clk_is_enabled);
void svip_sys2_clk_enable(u32 mask)
{
sys2_w32(sys2_r32(clksr) | mask, clkenr);
asm("sync;");
}
EXPORT_SYMBOL(svip_sys2_clk_enable);
int svip_sys2_clk_is_enabled(u32 mask)
{
return (sys2_r32(clksr) & mask) != 0;
}
EXPORT_SYMBOL(svip_sys2_clk_is_enabled);
int ltq_pms_read_procmem(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
long len = 0;
int t = 0;
u32 bit = 0;
u32 reg_tmp, bits_tmp;
len = sprintf(buf, "\nSVIP PMS Settings\n");
len = len + sprintf(buf + len,
" 3 2 1 0\n");
len = len + sprintf(buf + len,
" 210987654321098765432109876543210\n");
len = len + sprintf(buf + len,
"---------------------------------------------\n");
len = len + sprintf(buf + len,
"SYS1_CLKSR: ");
reg_tmp = sys1_r32(clksr);
bit = 0x80000000;
for (t = 31; t != -1; t--) {
bits_tmp = (reg_tmp & bit) >> t;
len = len + sprintf(buf + len, "%d", bits_tmp);
bit = bit >> 1;
}
len = len + sprintf(buf + len, "\n\n");
len = len + sprintf(buf + len, "SYS2_CLKSR: ");
reg_tmp = sys2_r32(clksr);
bit = 0x80000000;
for (t = 31; t != -1; t--) {
bits_tmp = (reg_tmp & bit) >> t;
len = len + sprintf(buf + len, "%d", bits_tmp);
bit = bit >> 1;
}
len = len + sprintf(buf + len, "\n\n");
*eof = 1;
return len;
}
int __init ltq_pms_init_proc(void)
{
return create_proc_read_entry("driver/ltq_pms", 0, NULL,
ltq_pms_read_procmem, NULL) == NULL;
}
module_init(ltq_pms_init_proc);

View File

@@ -0,0 +1,73 @@
/*
* 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.
*
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/time.h>
#include <asm/bootinfo.h>
#include <lantiq_soc.h>
#include "../prom.h"
#include "../clk.h"
#include "../machtypes.h"
#include <base_reg.h>
#include <ebu_reg.h>
#define SOC_SVIP "SVIP"
#define PART_SHIFT 12
#define PART_MASK 0x0FFFF000
#define REV_SHIFT 28
#define REV_MASK 0xF0000000
static struct svip_reg_ebu *const ebu = (struct svip_reg_ebu *)LTQ_EBU_BASE;
void __init ltq_soc_init(void)
{
clkdev_add_static(ltq_svip_cpu_hz(), ltq_svip_fpi_hz(),
ltq_svip_io_region_clock());
}
void __init
ltq_soc_setup(void)
{
if (mips_machtype == LANTIQ_MACH_EASY33016 ||
mips_machtype == LANTIQ_MACH_EASY336) {
ebu_w32(0x120000f1, addr_sel_2);
ebu_w32(LTQ_EBU_CON_0_ADSWP |
LTQ_EBU_CON_0_SETUP |
LTQ_EBU_CON_0_BCGEN_VAL(0x02) |
LTQ_EBU_CON_0_WAITWRC_VAL(7) |
LTQ_EBU_CON_0_WAITRDC_VAL(3) |
LTQ_EBU_CON_0_HOLDC_VAL(3) |
LTQ_EBU_CON_0_RECOVC_VAL(3) |
LTQ_EBU_CON_0_CMULT_VAL(3), con_2);
}
}
void __init
ltq_soc_detect(struct ltq_soc_info *i)
{
i->partnum = (ltq_r32(LTQ_STATUS_CHIPID) & PART_MASK) >> PART_SHIFT;
i->rev = (ltq_r32(LTQ_STATUS_CHIPID) & REV_MASK) >> REV_SHIFT;
sprintf(i->rev_type, "1.%d", i->rev);
switch (i->partnum) {
case SOC_ID_SVIP:
i->name = SOC_SVIP;
i->type = SOC_TYPE_SVIP;
break;
default:
printk(KERN_ERR "unknown partnum : 0x%08X\n", i->partnum);
while (1);
break;
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/pm.h>
#include <linux/module.h>
#include <asm/reboot.h>
#include <lantiq_soc.h>
#include "../machtypes.h"
#include <base_reg.h>
#include <sys1_reg.h>
#include <boot_reg.h>
#include <ebu_reg.h>
static struct svip_reg_sys1 *const sys1 = (struct svip_reg_sys1 *)LTQ_SYS1_BASE;
static struct svip_reg_ebu *const ebu = (struct svip_reg_ebu *)LTQ_EBU_BASE;
#define CPLD_CMDREG3 ((volatile unsigned char*)(KSEG1 + 0x120000f3))
extern void switchip_reset(void);
static void ltq_machine_restart(char *command)
{
printk(KERN_NOTICE "System restart\n");
local_irq_disable();
if (mips_machtype == LANTIQ_MACH_EASY33016 ||
mips_machtype == LANTIQ_MACH_EASY336) {
/* We just use the CPLD function to reset the entire system as a
workaround for the switch reset problem */
local_irq_disable();
ebu_w32(0x120000f1, addr_sel_2);
ebu_w32(0x404027ff, con_2);
if (mips_machtype == LANTIQ_MACH_EASY336)
/* set bit 0 to reset SVIP */
*CPLD_CMDREG3 = (1<<0);
else
/* set bit 7 to reset SVIP, set bit 3 to reset xT */
*CPLD_CMDREG3 = (1<<7) | (1<<3);
} else {
*LTQ_BOOT_RVEC(0) = 0;
/* reset all except PER, SUBSYS and CPU0 */
sys1_w32(0x00043F3E, rreqr);
/* release WDT0 reset */
sys1_w32(0x00000100, rrlsr);
/* restore reset value for clock enables */
sys1_w32(~0x0c000040, clkclr);
/* reset SUBSYS (incl. DDR2) and CPU0 */
sys1_w32(0x00030001, rbtr);
}
for (;;)
;
}
static void ltq_machine_halt(void)
{
printk(KERN_NOTICE "System halted.\n");
local_irq_disable();
for (;;)
;
}
static void ltq_machine_power_off(void)
{
printk(KERN_NOTICE "Please turn off the power now.\n");
local_irq_disable();
for (;;)
;
}
/* This function is used by the watchdog driver */
int ltq_reset_cause(void)
{
return 0;
}
EXPORT_SYMBOL_GPL(ltq_reset_cause);
static int __init mips_reboot_setup(void)
{
_machine_restart = ltq_machine_restart;
_machine_halt = ltq_machine_halt;
pm_power_off = ltq_machine_power_off;
return 0;
}
arch_initcall(mips_reboot_setup);

View File

@@ -0,0 +1,666 @@
/******************************************************************************
Copyright (c) 2007, Infineon Technologies. All rights reserved.
No Warranty
Because the program is licensed free of charge, there is no warranty for
the program, to the extent permitted by applicable law. Except when
otherwise stated in writing the copyright holders and/or other parties
provide the program "as is" without warranty of any kind, either
expressed or implied, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose. The
entire risk as to the quality and performance of the program is with
you. should the program prove defective, you assume the cost of all
necessary servicing, repair or correction.
In no event unless required by applicable law or agreed to in writing
will any copyright holder, or any other party who may modify and/or
redistribute the program as permitted above, be liable to you for
damages, including any general, special, incidental or consequential
damages arising out of the use or inability to use the program
(including but not limited to loss of data or data being rendered
inaccurate or losses sustained by you or third parties or a failure of
the program to operate with any other programs), even if such holder or
other party has been advised of the possibility of such damages.
******************************************************************************
Module : switchip_setup.c
Date : 2007-11-09
Description : Basic setup of embedded ethernet switch "SwitchIP"
Remarks: andreas.schmidt@infineon.com
*****************************************************************************/
/* TODO: get rid of #ifdef CONFIG_LANTIQ_MACH_EASY336 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/time.h>
#include <base_reg.h>
#include <es_reg.h>
#include <sys1_reg.h>
#include <dma_reg.h>
#include <lantiq_soc.h>
static struct svip_reg_sys1 *const sys1 = (struct svip_reg_sys1 *)LTQ_SYS1_BASE;
static struct svip_reg_es *const es = (struct svip_reg_es *)LTQ_ES_BASE;
/* PHY Organizationally Unique Identifier (OUI) */
#define PHY_OUI_PMC 0x00E004
#define PHY_OUI_VITESSE 0x008083
#define PHY_OUI_DEFAULT 0xFFFFFF
unsigned short switchip_phy_read(unsigned int phyaddr, unsigned int regaddr);
void switchip_phy_write(unsigned int phyaddr, unsigned int regaddr,
unsigned short data);
static int phy_address[2] = {0, 1};
static u32 phy_oui;
static void switchip_mdio_poll_init(void);
static void _switchip_mdio_poll(struct work_struct *work);
/* struct workqueue_struct mdio_poll_task; */
static struct workqueue_struct *mdio_poll_workqueue;
DECLARE_DELAYED_WORK(mdio_poll_work, _switchip_mdio_poll);
static int old_link_status[2] = {-1, -1};
/**
* Autonegotiation check.
* This funtion checks for link changes. If a link change has occured it will
* update certain switch registers.
*/
static void _switchip_check_phy_status(int port)
{
int new_link_status;
unsigned short reg1;
reg1 = switchip_phy_read(phy_address[port], 1);
if ((reg1 == 0xFFFF) || (reg1 == 0x0000))
return; /* no PHY connected */
new_link_status = reg1 & 4;
if (old_link_status[port] ^ new_link_status) {
/* link status change */
if (!new_link_status) {
if (port == 0)
es_w32_mask(LTQ_ES_P0_CTL_REG_FLP, 0, p0_ctl);
else
es_w32_mask(LTQ_ES_P0_CTL_REG_FLP, 0, p1_ctl);
/* read again; link bit is latched low! */
reg1 = switchip_phy_read(phy_address[port], 1);
new_link_status = reg1 & 4;
}
if (new_link_status) {
unsigned short reg0, reg4, reg5, reg9, reg10;
int phy_pause, phy_speed, phy_duplex;
int aneg_enable, aneg_cmpt;
reg0 = switchip_phy_read(phy_address[port], 0);
reg4 = switchip_phy_read(phy_address[port], 4);
aneg_enable = reg0 & 0x1000;
aneg_cmpt = reg1 & 0x20;
if (aneg_enable && aneg_cmpt) {
reg5 = switchip_phy_read(phy_address[port], 5);
switch (phy_oui) {
#ifdef CONFIG_LANTIQ_MACH_EASY336
case PHY_OUI_PMC:
/* PMC Sierra supports 1Gigabit FD,
* only. On successful
* auto-negotiation, we are sure this
* is what the LP can. */
phy_pause = ((reg4 & reg5) & 0x0080) >> 7;
phy_speed = 2;
phy_duplex = 1;
break;
#endif
case PHY_OUI_VITESSE:
case PHY_OUI_DEFAULT:
reg9 = switchip_phy_read(phy_address[port], 9);
reg10 = switchip_phy_read(phy_address[port], 10);
/* Check if advertise and partner
* agree on pause */
phy_pause = ((reg4 & reg5) & 0x0400) >> 10;
/* Find the best mode both partners
* support
* Priority: 1GB-FD, 1GB-HD, 100MB-FD,
* 100MB-HD, 10MB-FD, 10MB-HD */
phy_speed = ((((reg9<<2) & reg10)
& 0x0c00) >> 6) |
(((reg4 & reg5) & 0x01e0) >> 5);
if (phy_speed >= 0x0020) {
phy_speed = 2;
phy_duplex = 1;
} else if (phy_speed >= 0x0010) {
phy_speed = 2;
phy_duplex = 0;
} else if (phy_speed >= 0x0008) {
phy_speed = 1;
phy_duplex = 1;
} else if (phy_speed >= 0x0004) {
phy_speed = 1;
phy_duplex = 0;
} else if (phy_speed >= 0x0002) {
phy_speed = 0;
phy_duplex = 1;
} else {
phy_speed = 0;
phy_duplex = 0;
}
break;
default:
phy_pause = (reg4 & 0x0400) >> 10;
phy_speed = (reg0 & 0x40 ? 2 : (reg0 >> 13)&1);
phy_duplex = (reg0 >> 8)&1;
break;
}
} else {
/* parallel detection or fixed speed */
phy_pause = (reg4 & 0x0400) >> 10;
phy_speed = (reg0 & 0x40 ? 2 : (reg0 >> 13)&1);
phy_duplex = (reg0 >> 8)&1;
}
if (port == 0) {
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0SPD,
LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(phy_speed),
rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0DUP,
LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(phy_duplex),
rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0FCE,
LTQ_ES_RGMII_CTL_REG_P0FCE_VAL(phy_pause),
rgmii_ctl);
es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl);
} else {
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1SPD,
LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(phy_speed),
rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1DUP,
LTQ_ES_RGMII_CTL_REG_P1DUP_VAL(phy_duplex),
rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1FCE,
LTQ_ES_RGMII_CTL_REG_P0FCE_VAL(phy_pause),
rgmii_ctl);
es_w32_mask(1, LTQ_ES_P0_CTL_REG_FLP, p1_ctl);
}
}
}
old_link_status[port] = new_link_status;
}
static void _switchip_mdio_poll(struct work_struct *work)
{
if (es_r32(sw_gctl0) & LTQ_ES_SW_GCTL0_REG_SE) {
_switchip_check_phy_status(0);
_switchip_check_phy_status(1);
}
queue_delayed_work(mdio_poll_workqueue, &mdio_poll_work, HZ/2);
}
static void switchip_mdio_poll_init(void)
{
mdio_poll_workqueue = create_workqueue("SVIP MDIP poll");
INIT_DELAYED_WORK(&mdio_poll_work, _switchip_mdio_poll);
queue_delayed_work(mdio_poll_workqueue, &mdio_poll_work, HZ/2);
}
unsigned short switchip_phy_read(unsigned int phyaddr, unsigned int regaddr)
{
/* TODO: protect MDIO access with semaphore */
es_w32(LTQ_ES_MDIO_CTL_REG_MBUSY
| LTQ_ES_MDIO_CTL_REG_OP_VAL(2) /* read operation */
| LTQ_ES_MDIO_CTL_REG_PHYAD_VAL(phyaddr)
| LTQ_ES_MDIO_CTL_REG_REGAD_VAL(regaddr), mdio_ctl);
while (es_r32(mdio_ctl) & LTQ_ES_MDIO_CTL_REG_MBUSY);
return es_r32(mdio_data) & 0xFFFF;
}
EXPORT_SYMBOL(switchip_phy_read);
void switchip_phy_write(unsigned int phyaddr, unsigned int regaddr,
unsigned short data)
{
/* TODO: protect MDIO access with semaphore */
es_w32(LTQ_ES_MDIO_CTL_REG_WD_VAL(data)
| LTQ_ES_MDIO_CTL_REG_MBUSY
| LTQ_ES_MDIO_CTL_REG_OP_VAL(1) /* write operation */
| LTQ_ES_MDIO_CTL_REG_PHYAD_VAL(phyaddr)
| LTQ_ES_MDIO_CTL_REG_REGAD_VAL(regaddr), mdio_ctl);
while (es_r32(mdio_ctl) & LTQ_ES_MDIO_CTL_REG_MBUSY);
return;
}
EXPORT_SYMBOL(switchip_phy_write);
const static u32 switch_reset_offset_000[] = {
/*b8000000:*/ 0xffffffff, 0x00000001, 0x00000001, 0x00000003,
/*b8000010:*/ 0x04070001, 0x04070001, 0x04070001, 0xffffffff,
/*b8000020:*/ 0x00001be8, 0x00001be8, 0x00001be8, 0xffffffff,
/*b8000030:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001,
/*b8000040:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001,
/*b8000050:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001,
/*b8000060:*/ 0x00000000, 0x00000000, 0x00081000, 0x001f7777,
/*b8000070:*/ 0x00000000, 0x00000000, 0x0c00ac2b, 0x0000fa50,
/*b8000080:*/ 0x00001000, 0x00001800, 0x00000000, 0x00000000,
/*b8000090:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b80000a0:*/ 0x00000000, 0x00000050, 0x00000010, 0x00000000,
/*b80000b0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b80000c0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b80000d0:*/ 0xffffffff, 0x00000000, 0x00000000
};
const static u32 switch_reset_offset_100[] = {
/*b8000100:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000110:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000120:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000130:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000140:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000150:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000160:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000170:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000180:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b8000190:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b80001a0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
/*b80001b0:*/ 0x00000000, 0x00000000
};
/*
* Switch Reset.
*/
void switchip_reset(void)
{
volatile unsigned int *reg;
volatile unsigned int rdreg;
int i;
sys1_w32(SYS1_CLKENR_ETHSW, clkenr);
asm("sync");
/* disable P0 */
es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p0_ctl);
/* disable P1 */
es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p1_ctl);
/* disable P2 */
es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p2_ctl);
/**************************************
* BEGIN: Procedure to clear MAC table
**************************************/
for (i = 0; i < 3; i++) {
int result;
/* check if access engine is available */
while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY);
/* initialise to first address */
es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(3)
| LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(0), adr_tb_ctl2);
/* wait while busy */
while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY);
/* setup the portmap */
es_w32_mask(0, LTQ_ES_ADR_TB_CTL1_REG_PMAP_VAL(1 << i),
adr_tb_ctl1);
do {
/* search for addresses by port */
es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(2)
| LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(9), adr_tb_ctl2);
/* wait while busy */
while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY);
result = LTQ_ES_ADR_TB_ST2_REG_RSLT_GET(es_r32(adr_tb_st2));
if (result == 0x101) {
printk(KERN_ERR "%s, cmd error\n", __func__);
return;
}
/* if Command OK, address found... */
if (result == 0) {
unsigned char mac[6];
mac[5] = (es_r32(adr_tb_st0) >> 0) & 0xff;
mac[4] = (es_r32(adr_tb_st0) >> 8) & 0xff;
mac[3] = (es_r32(adr_tb_st0) >> 16) & 0xff;
mac[2] = (es_r32(adr_tb_st0) >> 24) & 0xff;
mac[1] = (es_r32(adr_tb_st1) >> 0) & 0xff;
mac[0] = (es_r32(adr_tb_st1) >> 8) & 0xff;
/* setup address */
es_w32((mac[5] << 0) |
(mac[4] << 8) |
(mac[3] << 16) |
(mac[2] << 24), adr_tb_ctl0);
es_w32(LTQ_ES_ADR_TB_CTL1_REG_PMAP_VAL(1<<i) |
LTQ_ES_ADR_TB_CTL1_REG_FID_VAL(0) |
(mac[0] << 8) |
(mac[1] << 0), adr_tb_ctl1);
/* erase address */
es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(1) |
LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(15),
adr_tb_ctl2);
/* wait, while busy */
while (es_r32(adr_tb_st2) &
LTQ_ES_ADR_TB_ST2_REG_BUSY);
}
} while (result == 0);
}
/**************************************
* END: Procedure to clear MAC table
**************************************/
/* reset RMON counters */
es_w32(LTQ_ES_RMON_CTL_REG_BAS | LTQ_ES_RMON_CTL_REG_CAC_VAL(3),
rmon_ctl);
/* bring all registers to reset state */
reg = LTQ_ES_PS_REG;
for (i = 0; i < ARRAY_SIZE(switch_reset_offset_000); i++) {
if ((reg == LTQ_ES_PS_REG) ||
(reg >= LTQ_ES_ADR_TB_CTL0_REG &&
reg <= LTQ_ES_ADR_TB_ST2_REG))
continue;
if (switch_reset_offset_000[i] != 0xFFFFFFFF) {
/* write reset value to register */
*reg = switch_reset_offset_000[i];
/* read register value back */
rdreg = *reg;
if (reg == LTQ_ES_SW_GCTL1_REG)
rdreg &= ~LTQ_ES_SW_GCTL1_REG_BISTDN;
/* compare read value with written one */
if (rdreg != switch_reset_offset_000[i]) {
printk(KERN_ERR "%s,%d: reg %08x mismatch "
"[has:%08x, expect:%08x]\n",
__func__, __LINE__,
(unsigned int)reg, rdreg,
switch_reset_offset_000[i]);
}
}
reg++;
}
reg = LTQ_ES_VLAN_FLT0_REG;
for (i = 0; i < ARRAY_SIZE(switch_reset_offset_100); i++) {
*reg = switch_reset_offset_100[i];
rdreg = *reg;
if (rdreg != switch_reset_offset_100[i]) {
printk(KERN_ERR "%s,%d: reg %08x mismatch "
"[has:%08x, expect:%08x]\n", __func__, __LINE__,
(unsigned int)reg, rdreg,
switch_reset_offset_100[i]);
}
reg++;
}
}
EXPORT_SYMBOL(switchip_reset);
static u32 get_phy_oui(unsigned char phy_addr)
{
u32 oui;
int i, bit, byte, shift, w;
u16 reg_id[2];
/* read PHY identifier registers 1 and 2 */
reg_id[0] = switchip_phy_read(phy_addr, 2);
reg_id[1] = switchip_phy_read(phy_addr, 3);
oui = 0;
w = 1;
shift = 7;
byte = 1;
for (i = 0, bit = 10; i <= 21; i++, bit++) {
oui |= ((reg_id[w] & (1<<bit)) ? 1 : 0) << shift;
if (!(shift % 8)) {
byte++;
if (byte == 2)
shift = 15;
else
shift = 21;
} else {
shift--;
}
if (w == 1 && bit == 15) {
bit = -1;
w = 0;
}
}
return oui;
}
/*
* Switch Initialization.
*/
int switchip_init(void)
{
int eth_port, phy_present = 0;
u16 reg, mode;
sys1_w32(SYS1_CLKENR_ETHSW, clkenr);
asm("sync");
/* Enable Switch, if not already done so */
if ((es_r32(sw_gctl0) & LTQ_ES_SW_GCTL0_REG_SE) == 0)
es_w32_mask(0, LTQ_ES_SW_GCTL0_REG_SE, sw_gctl0);
/* Wait for completion of MBIST */
while (LTQ_ES_SW_GCTL1_REG_BISTDN_GET(es_r32(sw_gctl1)) == 0);
switchip_reset();
mode = LTQ_ES_RGMII_CTL_REG_IS_GET(es_r32(rgmii_ctl));
eth_port = (mode == 2 ? 1 : 0);
/* Set the primary port(port toward backplane) as sniffer port,
changing from P2 which is the reset setting */
es_w32_mask(LTQ_ES_SW_GCTL0_REG_SNIFFPN,
LTQ_ES_SW_GCTL0_REG_SNIFFPN_VAL(eth_port),
sw_gctl0);
/* Point MDIO state machine to invalid PHY addresses 8 and 9 */
es_w32_mask(0, LTQ_ES_SW_GCTL0_REG_PHYBA, sw_gctl0);
/* Add CRC for packets from DMA to PMAC.
Remove CRC for packets from PMAC to DMA. */
es_w32(LTQ_ES_PMAC_HD_CTL_RC | LTQ_ES_PMAC_HD_CTL_AC, pmac_hd_ctl);
phy_oui = get_phy_oui(0);
switch (phy_oui) {
#ifdef CONFIG_LANTIQ_MACH_EASY336
case PHY_OUI_PMC:
phy_address[0] = (mode == 2 ? -1 : 2);
phy_address[1] = (mode == 2 ? 2 : -1);
break;
#endif
case PHY_OUI_VITESSE:
default:
phy_oui = PHY_OUI_DEFAULT;
phy_address[0] = (mode == 2 ? 1 : 0);
phy_address[1] = (mode == 2 ? 0 : 1);
break;
}
/****** PORT 0 *****/
reg = switchip_phy_read(phy_address[0], 1);
if ((reg != 0x0000) && (reg != 0xffff)) {
/* PHY connected? */
phy_present |= 1;
/* Set Rx- and TxDelay in case of RGMII */
switch (mode) {
case 0: /* *RGMII,RGMII */
case 2: /* RGMII,*GMII */
/* program clock delay in PHY, not in SVIP */
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0RDLY, 0, rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0TDLY, 0, rgmii_ctl);
if (phy_oui == PHY_OUI_VITESSE ||
phy_oui == PHY_OUI_DEFAULT) {
switchip_phy_write(phy_address[0], 31, 0x0001);
switchip_phy_write(phy_address[0], 28, 0xA000);
switchip_phy_write(phy_address[0], 31, 0x0000);
}
default:
break;
}
if (phy_oui == PHY_OUI_VITESSE ||
phy_oui == PHY_OUI_DEFAULT) {
/* Program PHY advertisements and
* restart auto-negotiation */
switchip_phy_write(phy_address[0], 4, 0x05E1);
switchip_phy_write(phy_address[0], 9, 0x0300);
switchip_phy_write(phy_address[0], 0, 0x3300);
} else {
reg = switchip_phy_read(phy_address[1], 0);
reg |= 0x1000; /* auto-negotiation enable */
switchip_phy_write(phy_address[1], 0, reg);
reg |= 0x0200; /* auto-negotiation restart */
switchip_phy_write(phy_address[1], 0, reg);
}
} else {
/* Force SWITCH link with highest capability:
* 100M FD for MII
* 1G FD for GMII/RGMII
*/
switch (mode) {
case 1: /* *MII,MII */
case 3: /* *MII,RGMII */
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(1),
rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(1),
rgmii_ctl);
break;
case 0: /* *RGMII,RGMII */
case 2: /* RGMII,*GMII */
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(2),
rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(1),
rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0RDLY, 0, rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0TDLY_VAL(2),
rgmii_ctl);
break;
}
es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl);
}
/****** PORT 1 *****/
reg = switchip_phy_read(phy_address[1], 1);
if ((reg != 0x0000) && (reg != 0xffff)) {
/* PHY connected? */
phy_present |= 2;
/* Set Rx- and TxDelay in case of RGMII */
switch (mode) {
case 0: /* *RGMII,RGMII */
case 3: /* *MII,RGMII */
/* program clock delay in PHY, not in SVIP */
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1RDLY, 0, rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1TDLY, 0, rgmii_ctl);
if (phy_oui == PHY_OUI_VITESSE ||
phy_oui == PHY_OUI_DEFAULT) {
switchip_phy_write(phy_address[1], 31, 0x0001);
switchip_phy_write(phy_address[1], 28, 0xA000);
switchip_phy_write(phy_address[1], 31, 0x0000);
}
break;
case 2: /* RGMII,*GMII */
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2),
rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
#ifdef CONFIG_LANTIQ_MACH_EASY336
if (phy_oui == PHY_OUI_PMC) {
switchip_phy_write(phy_address[1], 24, 0x0510);
switchip_phy_write(phy_address[1], 17, 0xA38C);
switchip_phy_write(phy_address[1], 17, 0xA384);
}
#endif
break;
default:
break;
}
/* Program PHY advertisements and restart auto-negotiation */
if (phy_oui == PHY_OUI_VITESSE ||
phy_oui == PHY_OUI_DEFAULT) {
switchip_phy_write(phy_address[1], 4, 0x05E1);
switchip_phy_write(phy_address[1], 9, 0x0300);
switchip_phy_write(phy_address[1], 0, 0x3300);
} else {
reg = switchip_phy_read(phy_address[1], 0);
reg |= 0x1000; /* auto-negotiation enable */
switchip_phy_write(phy_address[1], 0, reg);
reg |= 0x0200; /* auto-negotiation restart */
switchip_phy_write(phy_address[1], 0, reg);
}
} else {
/* Force SWITCH link with highest capability:
* 100M FD for MII
* 1G FD for GMII/RGMII
*/
switch (mode) {
case 1: /* *MII,MII */
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(1),
rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
break;
case 0: /* *RGMII,RGMII */
case 3: /* *MII,RGMII */
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2),
rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1RDLY, 0, rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1TDLY_VAL(2),
rgmii_ctl);
break;
case 2: /* RGMII,*GMII */
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2),
rgmii_ctl);
es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
break;
}
es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl);
}
/*
* Allow unknown unicast/multicast and broadcasts
* on all ports.
*/
es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_UP_VAL(7), sw_gctl1);
es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_BP_VAL(7), sw_gctl1);
es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_MP_VAL(7), sw_gctl1);
es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_RP_VAL(7), sw_gctl1);
/* Enable LAN port(s) */
if (eth_port == 0)
es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p0_ctl);
else
es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p1_ctl);
/* Enable CPU Port (Forwarding State) */
es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p2_ctl);
if (phy_present)
switchip_mdio_poll_init();
return 0;
}
EXPORT_SYMBOL(switchip_init);
device_initcall(switchip_init);

View File

@@ -0,0 +1,329 @@
/*
* 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) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/io.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <asm/time.h>
#include <asm/irq.h>
#include <asm/div64.h>
#include <lantiq_soc.h>
#include "../clk.h"
static unsigned int ltq_ram_clocks[] = {
CLOCK_167M, CLOCK_133M, CLOCK_111M, CLOCK_83M };
#define DDR_HZ ltq_ram_clocks[ltq_cgu_r32(LTQ_CGU_SYS) & 0x3]
#define BASIC_FREQUENCY_1 35328000
#define BASIC_FREQUENCY_2 36000000
#define BASIS_REQUENCY_USB 12000000
#define GET_BITS(x, msb, lsb) \
(((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb))
/* legacy xway clock */
#define LTQ_CGU_PLL0_CFG 0x0004
#define LTQ_CGU_PLL1_CFG 0x0008
#define LTQ_CGU_PLL2_CFG 0x000C
#define LTQ_CGU_SYS 0x0010
#define LTQ_CGU_UPDATE 0x0014
#define LTQ_CGU_IF_CLK 0x0018
#define LTQ_CGU_OSC_CON 0x001C
#define LTQ_CGU_SMD 0x0020
#define LTQ_CGU_CT1SR 0x0028
#define LTQ_CGU_CT2SR 0x002C
#define LTQ_CGU_PCMCR 0x0030
#define LTQ_CGU_PCI_CR 0x0034
#define LTQ_CGU_PD_PC 0x0038
#define LTQ_CGU_FMR 0x003C
#define CGU_PLL0_PHASE_DIVIDER_ENABLE \
(ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 31))
#define CGU_PLL0_BYPASS \
(ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 30))
#define CGU_PLL0_CFG_DSMSEL \
(ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 28))
#define CGU_PLL0_CFG_FRAC_EN \
(ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 27))
#define CGU_PLL1_SRC \
(ltq_cgu_r32(LTQ_CGU_PLL1_CFG) & (1 << 31))
#define CGU_PLL2_PHASE_DIVIDER_ENABLE \
(ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & (1 << 20))
#define CGU_SYS_FPI_SEL (1 << 6)
#define CGU_SYS_DDR_SEL 0x3
#define CGU_PLL0_SRC (1 << 29)
#define CGU_PLL0_CFG_PLLK GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 26, 17)
#define CGU_PLL0_CFG_PLLN GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 12, 6)
#define CGU_PLL0_CFG_PLLM GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 5, 2)
#define CGU_PLL2_SRC GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 18, 17)
#define CGU_PLL2_CFG_INPUT_DIV GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 16, 13)
/* vr9 clock */
#define LTQ_CGU_SYS_VR9 0x0c
#define LTQ_CGU_IF_CLK_VR9 0x24
static unsigned int ltq_get_pll0_fdiv(void);
static inline unsigned int get_input_clock(int pll)
{
switch (pll) {
case 0:
if (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & CGU_PLL0_SRC)
return BASIS_REQUENCY_USB;
else if (CGU_PLL0_PHASE_DIVIDER_ENABLE)
return BASIC_FREQUENCY_1;
else
return BASIC_FREQUENCY_2;
case 1:
if (CGU_PLL1_SRC)
return BASIS_REQUENCY_USB;
else if (CGU_PLL0_PHASE_DIVIDER_ENABLE)
return BASIC_FREQUENCY_1;
else
return BASIC_FREQUENCY_2;
case 2:
switch (CGU_PLL2_SRC) {
case 0:
return ltq_get_pll0_fdiv();
case 1:
return CGU_PLL2_PHASE_DIVIDER_ENABLE ?
BASIC_FREQUENCY_1 :
BASIC_FREQUENCY_2;
case 2:
return BASIS_REQUENCY_USB;
}
default:
return 0;
}
}
static inline unsigned int cal_dsm(int pll, unsigned int num, unsigned int den)
{
u64 res, clock = get_input_clock(pll);
res = num * clock;
do_div(res, den);
return res;
}
static inline unsigned int mash_dsm(int pll, unsigned int M, unsigned int N,
unsigned int K)
{
unsigned int num = ((N + 1) << 10) + K;
unsigned int den = (M + 1) << 10;
return cal_dsm(pll, num, den);
}
static inline unsigned int ssff_dsm_1(int pll, unsigned int M, unsigned int N,
unsigned int K)
{
unsigned int num = ((N + 1) << 11) + K + 512;
unsigned int den = (M + 1) << 11;
return cal_dsm(pll, num, den);
}
static inline unsigned int ssff_dsm_2(int pll, unsigned int M, unsigned int N,
unsigned int K)
{
unsigned int num = K >= 512 ?
((N + 1) << 12) + K - 512 : ((N + 1) << 12) + K + 3584;
unsigned int den = (M + 1) << 12;
return cal_dsm(pll, num, den);
}
static inline unsigned int dsm(int pll, unsigned int M, unsigned int N,
unsigned int K, unsigned int dsmsel, unsigned int phase_div_en)
{
if (!dsmsel)
return mash_dsm(pll, M, N, K);
else if (!phase_div_en)
return mash_dsm(pll, M, N, K);
else
return ssff_dsm_2(pll, M, N, K);
}
static inline unsigned int ltq_get_pll0_fosc(void)
{
if (CGU_PLL0_BYPASS)
return get_input_clock(0);
else
return !CGU_PLL0_CFG_FRAC_EN
? dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN, 0,
CGU_PLL0_CFG_DSMSEL,
CGU_PLL0_PHASE_DIVIDER_ENABLE)
: dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN,
CGU_PLL0_CFG_PLLK, CGU_PLL0_CFG_DSMSEL,
CGU_PLL0_PHASE_DIVIDER_ENABLE);
}
static unsigned int ltq_get_pll0_fdiv(void)
{
unsigned int div = CGU_PLL2_CFG_INPUT_DIV + 1;
return (ltq_get_pll0_fosc() + (div >> 1)) / div;
}
unsigned long ltq_danube_io_region_clock(void)
{
unsigned int ret = ltq_get_pll0_fosc();
switch (ltq_cgu_r32(LTQ_CGU_SYS) & 0x3) {
default:
case 0:
return (ret + 1) / 2;
case 1:
return (ret * 2 + 2) / 5;
case 2:
return (ret + 1) / 3;
case 3:
return (ret + 2) / 4;
}
}
unsigned long ltq_danube_fpi_bus_clock(int fpi)
{
unsigned long ret = ltq_danube_io_region_clock();
if ((fpi == 2) && (ltq_cgu_r32(LTQ_CGU_SYS) & CGU_SYS_FPI_SEL))
ret >>= 1;
return ret;
}
unsigned long ltq_danube_fpi_hz(void)
{
unsigned long ddr_clock = DDR_HZ;
if (ltq_cgu_r32(LTQ_CGU_SYS) & 0x40)
return ddr_clock >> 1;
return ddr_clock;
}
unsigned long ltq_danube_cpu_hz(void)
{
switch (ltq_cgu_r32(LTQ_CGU_SYS) & 0xc) {
case 0:
return CLOCK_333M;
case 4:
return DDR_HZ;
case 8:
return DDR_HZ << 1;
default:
return DDR_HZ >> 1;
}
}
unsigned long ltq_ar9_sys_hz(void)
{
if (((ltq_cgu_r32(LTQ_CGU_SYS) >> 3) & 0x3) == 0x2)
return CLOCK_393M;
return CLOCK_333M;
}
unsigned long ltq_ar9_fpi_hz(void)
{
unsigned long sys = ltq_ar9_sys_hz();
if (ltq_cgu_r32(LTQ_CGU_SYS) & BIT(0))
return sys;
return sys >> 1;
}
unsigned long ltq_ar9_cpu_hz(void)
{
if (ltq_cgu_r32(LTQ_CGU_SYS) & BIT(2))
return ltq_ar9_fpi_hz();
else
return ltq_ar9_sys_hz();
}
unsigned long ltq_vr9_cpu_hz(void)
{
unsigned int cpu_sel;
unsigned long clk;
cpu_sel = (ltq_cgu_r32(LTQ_CGU_SYS_VR9) >> 4) & 0xf;
switch (cpu_sel) {
case 0:
clk = CLOCK_600M;
break;
case 1:
clk = CLOCK_500M;
break;
case 2:
clk = CLOCK_393M;
break;
case 3:
clk = CLOCK_333M;
break;
case 5:
case 6:
clk = CLOCK_196_608M;
break;
case 7:
clk = CLOCK_167M;
break;
case 4:
case 8:
case 9:
clk = CLOCK_125M;
break;
default:
clk = 0;
break;
}
return clk;
}
unsigned long ltq_vr9_fpi_hz(void)
{
unsigned int ocp_sel, cpu_clk;
unsigned long clk;
cpu_clk = ltq_vr9_cpu_hz();
ocp_sel = ltq_cgu_r32(LTQ_CGU_SYS_VR9) & 0x3;
switch (ocp_sel) {
case 0:
/* OCP ratio 1 */
clk = cpu_clk;
break;
case 2:
/* OCP ratio 2 */
clk = cpu_clk / 2;
break;
case 3:
/* OCP ratio 2.5 */
clk = (cpu_clk * 2) / 5;
break;
case 4:
/* OCP ratio 3 */
clk = cpu_clk / 3;
break;
default:
clk = 0;
break;
}
return clk;
}
unsigned long ltq_vr9_fpi_bus_clock(int fpi)
{
return ltq_vr9_fpi_hz();
}

View File

@@ -0,0 +1,45 @@
/*
* 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.
*
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/mtd/physmap.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/etherdevice.h>
#include <linux/reboot.h>
#include <linux/time.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <lantiq_soc.h>
#include <lantiq_irq.h>
#include <lantiq_platform.h>
static u64 dmamask = (u32)0x1fffffff;
static struct platform_device platform_dev = {
.name = "ifxusb_hcd",
.dev.dma_mask = &dmamask,
};
int __init
xway_register_hcd(int *pins)
{
platform_dev.dev.platform_data = pins;
return platform_device_register(&platform_dev);
}

View File

@@ -0,0 +1,17 @@
/*
* 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.
*
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
*/
#ifndef _LTQ_DEV_HCD_H__
#define _LTQ_DEV_HCD_H__
#include <lantiq_platform.h>
extern void __init xway_register_hcd(int *pin);
#endif

View File

@@ -0,0 +1,176 @@
/*
* 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) 2012 John Crispin <blogic@openwrt.org>
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/pm.h>
#include <linux/export.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/reboot.h>
#include <lantiq_soc.h>
#include "../clk.h"
#include "../devices.h"
#define ltq_gptu_w32(x, y) ltq_w32((x), ltq_gptu_membase + (y))
#define ltq_gptu_r32(x) ltq_r32(ltq_gptu_membase + (x))
/* the magic ID byte of the core */
#define GPTU_MAGIC 0x59
/* clock control register */
#define GPTU_CLC 0x00
/* id register */
#define GPTU_ID 0x08
/* interrupt node enable */
#define GPTU_IRNEN 0xf4
/* interrupt control register */
#define GPTU_IRCR 0xf8
/* interrupt capture register */
#define GPTU_IRNCR 0xfc
/* there are 3 identical blocks of 2 timers. calculate register offsets */
#define GPTU_SHIFT(x) (x % 2 ? 4 : 0)
#define GPTU_BASE(x) (((x >> 1) * 0x20) + 0x10)
/* timer control register */
#define GPTU_CON(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x00)
/* timer auto reload register */
#define GPTU_RUN(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x08)
/* timer manual reload register */
#define GPTU_RLD(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x10)
/* timer count register */
#define GPTU_CNT(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x18)
/* GPTU_CON(x) */
#define CON_CNT BIT(2)
#define CON_EDGE_FALL BIT(7)
#define CON_SYNC BIT(8)
#define CON_CLK_INT BIT(10)
/* GPTU_RUN(x) */
#define RUN_SEN BIT(0)
#define RUN_RL BIT(2)
/* set clock to runmode */
#define CLC_RMC BIT(8)
/* bring core out of suspend */
#define CLC_SUSPEND BIT(4)
/* the disable bit */
#define CLC_DISABLE BIT(0)
#define TIMER_INTERRUPT (INT_NUM_IM3_IRL0 + 22)
enum gptu_timer {
TIMER1A = 0,
TIMER1B,
TIMER2A,
TIMER2B,
TIMER3A,
TIMER3B
};
static struct resource ltq_gptu_resource =
MEM_RES("GPTU", LTQ_GPTU_BASE_ADDR, LTQ_GPTU_SIZE);
static void __iomem *ltq_gptu_membase;
static irqreturn_t timer_irq_handler(int irq, void *priv)
{
int timer = irq - TIMER_INTERRUPT;
ltq_gptu_w32(1 << timer, GPTU_IRNCR);
return IRQ_HANDLED;
}
static void gptu_hwinit(void)
{
struct clk *clk = clk_get_sys("ltq_gptu", NULL);
clk_enable(clk);
ltq_gptu_w32(0x00, GPTU_IRNEN);
ltq_gptu_w32(0xff, GPTU_IRNCR);
ltq_gptu_w32(CLC_RMC | CLC_SUSPEND, GPTU_CLC);
}
static void gptu_hwexit(void)
{
ltq_gptu_w32(0x00, GPTU_IRNEN);
ltq_gptu_w32(0xff, GPTU_IRNCR);
ltq_gptu_w32(CLC_DISABLE, GPTU_CLC);
}
static int ltq_gptu_enable(struct clk *clk)
{
int ret = request_irq(TIMER_INTERRUPT + clk->bits, timer_irq_handler,
IRQF_TIMER, "timer", NULL);
if (ret) {
pr_err("gptu: failed to request irq\n");
return ret;
}
ltq_gptu_w32(CON_CNT | CON_EDGE_FALL | CON_SYNC | CON_CLK_INT,
GPTU_CON(clk->bits));
ltq_gptu_w32(1, GPTU_RLD(clk->bits));
ltq_gptu_w32(ltq_gptu_r32(GPTU_IRNEN) | clk->bits, GPTU_IRNEN);
ltq_gptu_w32(RUN_SEN | RUN_RL, GPTU_RUN(clk->bits));
return 0;
}
static void ltq_gptu_disable(struct clk *clk)
{
ltq_gptu_w32(0, GPTU_RUN(clk->bits));
ltq_gptu_w32(0, GPTU_CON(clk->bits));
ltq_gptu_w32(0, GPTU_RLD(clk->bits));
ltq_gptu_w32(ltq_gptu_r32(GPTU_IRNEN) & ~clk->bits, GPTU_IRNEN);
free_irq(TIMER_INTERRUPT + clk->bits, NULL);
}
static inline void clkdev_add_gptu(const char *con, unsigned int timer)
{
struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
clk->cl.dev_id = "ltq_gptu";
clk->cl.con_id = con;
clk->cl.clk = clk;
clk->enable = ltq_gptu_enable;
clk->disable = ltq_gptu_disable;
clk->bits = timer;
clkdev_add(&clk->cl);
}
static int __init gptu_setup(void)
{
/* remap gptu register range */
ltq_gptu_membase = ltq_remap_resource(&ltq_gptu_resource);
if (!ltq_gptu_membase)
panic("Failed to remap gptu memory");
/* power up the core */
gptu_hwinit();
/* the gptu has a ID register */
if (((ltq_gptu_r32(GPTU_ID) >> 8) & 0xff) != GPTU_MAGIC) {
pr_err("gptu: failed to find magic\n");
gptu_hwexit();
return -ENAVAIL;
}
/* register the clocks */
clkdev_add_gptu("timer1a", TIMER1A);
clkdev_add_gptu("timer1b", TIMER1B);
clkdev_add_gptu("timer2a", TIMER2A);
clkdev_add_gptu("timer2b", TIMER2B);
clkdev_add_gptu("timer3a", TIMER3A);
clkdev_add_gptu("timer3b", TIMER3B);
pr_info("gptu: 6 timers loaded\n");
return 0;
}
arch_initcall(gptu_setup);

View File

@@ -1,58 +0,0 @@
/*
* 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) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/input.h>
#include <lantiq.h>
#include "../machtypes.h"
#include "devices.h"
static struct mtd_partition easy50601_partitions[] = {
{
.name = "uboot",
.offset = 0x0,
.size = 0x10000,
},
{
.name = "uboot_env",
.offset = 0x10000,
.size = 0x10000,
},
{
.name = "linux",
.offset = 0x20000,
.size = 0x3d0000,
},
};
static struct physmap_flash_data easy50601_flash_data = {
.nr_parts = ARRAY_SIZE(easy50601_partitions),
.parts = easy50601_partitions,
};
static struct ltq_eth_data ltq_eth_data = {
.mii_mode = -1, /* use EPHY */
};
static void __init
easy50601_init(void)
{
ltq_register_nor(&easy50601_flash_data);
ltq_register_etop(&ltq_eth_data);
}
MIPS_MACHINE(LTQ_MACH_EASY50601,
"EASY50601",
"EASY50601 Eval Board",
easy50601_init);

View File

@@ -1,71 +0,0 @@
/*
* 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) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/input.h>
#include <linux/phy.h>
#include <lantiq_soc.h>
#include <irq.h>
#include "../machtypes.h"
#include "devices.h"
static struct mtd_partition easy50712_partitions[] = {
{
.name = "uboot",
.offset = 0x0,
.size = 0x10000,
},
{
.name = "uboot_env",
.offset = 0x10000,
.size = 0x10000,
},
{
.name = "linux",
.offset = 0x20000,
.size = 0x3d0000,
},
};
static struct physmap_flash_data easy50712_flash_data = {
.nr_parts = ARRAY_SIZE(easy50712_partitions),
.parts = easy50712_partitions,
};
static struct ltq_pci_data ltq_pci_data = {
.clock = PCI_CLOCK_INT,
.gpio = PCI_GNT1 | PCI_REQ1,
.irq = {
[14] = INT_NUM_IM0_IRL0 + 22,
},
};
static struct ltq_eth_data ltq_eth_data = {
.mii_mode = PHY_INTERFACE_MODE_MII,
};
static void __init
easy50712_init(void)
{
ltq_register_gpio_stp();
ltq_register_nor(&easy50712_flash_data);
ltq_register_pci(&ltq_pci_data);
ltq_register_etop(&ltq_eth_data);
ltq_register_tapi();
}
MIPS_MACHINE(LTQ_MACH_EASY50712,
"EASY50712",
"EASY50712 Eval Board",
easy50712_init);

View File

@@ -0,0 +1,216 @@
/*
* 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) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/mtd/physmap.h>
#include <linux/mtd/nand.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <lantiq_soc.h>
#include <lantiq_irq.h>
#include <lantiq_platform.h>
#include "devices.h"
/* nand registers */
#define LTQ_EBU_NAND_WAIT 0xB4
#define LTQ_EBU_NAND_ECC0 0xB8
#define LTQ_EBU_NAND_ECC_AC 0xBC
#define LTQ_EBU_NAND_CON 0xB0
#define LTQ_EBU_ADDSEL1 0x24
/* gpio definitions */
#define PIN_ALE 13
#define PIN_CLE 24
#define PIN_CS1 23
#define PIN_RDY 48 /* NFLASH_READY */
#define PIN_RD 49 /* NFLASH_READ_N */
#define NAND_CMD_ALE (1 << 2)
#define NAND_CMD_CLE (1 << 3)
#define NAND_CMD_CS (1 << 4)
#define NAND_WRITE_CMD_RESET 0xff
#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE)
#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE)
#define NAND_WRITE_DATA (NAND_CMD_CS)
#define NAND_READ_DATA (NAND_CMD_CS)
#define NAND_WAIT_WR_C (1 << 3)
#define NAND_WAIT_RD (0x1)
#define ADDSEL1_MASK(x) (x << 4)
#define ADDSEL1_REGEN 1
#define BUSCON1_SETUP (1 << 22)
#define BUSCON1_BCGEN_RES (0x3 << 12)
#define BUSCON1_WAITWRC2 (2 << 8)
#define BUSCON1_WAITRDC2 (2 << 6)
#define BUSCON1_HOLDC1 (1 << 4)
#define BUSCON1_RECOVC1 (1 << 2)
#define BUSCON1_CMULT4 1
#define NAND_CON_NANDM 1
#define NAND_CON_CSMUX (1 << 1)
#define NAND_CON_CS_P (1 << 4)
#define NAND_CON_SE_P (1 << 5)
#define NAND_CON_WP_P (1 << 6)
#define NAND_CON_PRE_P (1 << 7)
#define NAND_CON_IN_CS0 0
#define NAND_CON_OUT_CS0 0
#define NAND_CON_IN_CS1 (1 << 8)
#define NAND_CON_OUT_CS1 (1 << 10)
#define NAND_CON_CE (1 << 20)
#define NAND_BASE_ADDRESS (KSEG1 | 0x14000000)
static const char *part_probes[] = { "cmdlinepart", NULL };
static void xway_select_chip(struct mtd_info *mtd, int chip)
{
switch (chip) {
case -1:
ltq_ebu_w32_mask(NAND_CON_CE, 0, LTQ_EBU_NAND_CON);
ltq_ebu_w32_mask(NAND_CON_NANDM, 0, LTQ_EBU_NAND_CON);
break;
case 0:
ltq_ebu_w32_mask(0, NAND_CON_NANDM, LTQ_EBU_NAND_CON);
ltq_ebu_w32_mask(0, NAND_CON_CE, LTQ_EBU_NAND_CON);
/* reset the nand chip */
while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
ltq_w32(NAND_WRITE_CMD_RESET,
((u32 *) (NAND_BASE_ADDRESS | NAND_WRITE_CMD)));
break;
default:
BUG();
}
}
static void xway_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
{
struct nand_chip *this = mtd->priv;
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_CLE)
this->IO_ADDR_W = (void __iomem *)
(NAND_BASE_ADDRESS | NAND_WRITE_CMD);
else if (ctrl & NAND_ALE)
this->IO_ADDR_W = (void __iomem *)
(NAND_BASE_ADDRESS | NAND_WRITE_ADDR);
}
if (data != NAND_CMD_NONE) {
*(volatile u8*) ((u32) this->IO_ADDR_W) = data;
while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
}
}
static int xway_dev_ready(struct mtd_info *mtd)
{
return ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_RD;
}
void nand_write(unsigned int addr, unsigned int val)
{
ltq_w32(val, ((u32 *) (NAND_BASE_ADDRESS | addr)));
while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
}
unsigned char xway_read_byte(struct mtd_info *mtd)
{
return ltq_r8((void __iomem *)(NAND_BASE_ADDRESS | (NAND_READ_DATA)));
}
static void xway_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++)
{
unsigned char res8 = ltq_r8((void __iomem *)(NAND_BASE_ADDRESS | (NAND_READ_DATA)));
buf[i] = res8;
}
}
static void xway_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++)
{
ltq_w8(buf[i], ((u32*)(NAND_BASE_ADDRESS | (NAND_WRITE_DATA))));
while((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0);
}
}
int xway_probe(struct platform_device *pdev)
{
/* might need this later ?
ltq_gpio_request(PIN_CS1, 2, 1, "NAND_CS1");
*/
ltq_gpio_request(&pdev->dev, PIN_CLE, 2, 1, "NAND_CLE");
ltq_gpio_request(&pdev->dev, PIN_ALE, 2, 1, "NAND_ALE");
if (ltq_is_ar9() || ltq_is_vr9()) {
ltq_gpio_request(&pdev->dev, PIN_RDY, 2, 0, "NAND_BSY");
ltq_gpio_request(&pdev->dev, PIN_RD, 2, 1, "NAND_RD");
}
ltq_ebu_w32((NAND_BASE_ADDRESS & 0x1fffff00)
| ADDSEL1_MASK(3) | ADDSEL1_REGEN, LTQ_EBU_ADDSEL1);
ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2
| BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1
| BUSCON1_CMULT4, LTQ_EBU_BUSCON1);
ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P
| NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P
| NAND_CON_IN_CS0 | NAND_CON_OUT_CS0, LTQ_EBU_NAND_CON);
ltq_w32(NAND_WRITE_CMD_RESET,
((u32 *) (NAND_BASE_ADDRESS | NAND_WRITE_CMD)));
while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
return 0;
}
static struct platform_nand_data falcon_flash_nand_data = {
.chip = {
.nr_chips = 1,
.chip_delay = 30,
.part_probe_types = part_probes,
},
.ctrl = {
.probe = xway_probe,
.cmd_ctrl = xway_cmd_ctrl,
.dev_ready = xway_dev_ready,
.select_chip = xway_select_chip,
.read_byte = xway_read_byte,
.read_buf = xway_read_buf,
.write_buf = xway_write_buf,
}
};
static struct resource ltq_nand_res =
MEM_RES("nand", 0x14000000, 0x7ffffff);
static struct platform_device ltq_flash_nand = {
.name = "gen_nand",
.id = -1,
.num_resources = 1,
.resource = &ltq_nand_res,
.dev = {
.platform_data = &falcon_flash_nand_data,
},
};
void __init xway_register_nand(struct mtd_partition *parts, int count)
{
falcon_flash_nand_data.chip.partitions = parts;
falcon_flash_nand_data.chip.nr_partitions = count;
platform_device_register(&ltq_flash_nand);
}

View File

@@ -0,0 +1,110 @@
/*
* 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) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/export.h>
#include <linux/clk.h>
#include <asm/bootinfo.h>
#include <asm/time.h>
#include <lantiq_soc.h>
#include "../prom.h"
#include "devices.h"
#define SOC_DANUBE "Danube"
#define SOC_TWINPASS "Twinpass"
#define SOC_AMAZON_SE "Amazon_SE"
#define SOC_AR9 "AR9"
#define SOC_GR9 "GR9"
#define SOC_VR9 "VR9"
#define PART_SHIFT 12
#define PART_MASK 0x0FFFFFFF
#define REV_SHIFT 28
#define REV_MASK 0xF0000000
void __init ltq_soc_detect(struct ltq_soc_info *i)
{
i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT;
i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT;
sprintf(i->rev_type, "1.%d", i->rev);
switch (i->partnum) {
case SOC_ID_DANUBE1:
case SOC_ID_DANUBE2:
i->name = SOC_DANUBE;
i->type = SOC_TYPE_DANUBE;
break;
case SOC_ID_TWINPASS:
i->name = SOC_TWINPASS;
i->type = SOC_TYPE_DANUBE;
break;
case SOC_ID_ARX188:
case SOC_ID_ARX168_1:
case SOC_ID_ARX168_2:
case SOC_ID_ARX182:
i->name = SOC_AR9;
i->type = SOC_TYPE_AR9;
break;
case SOC_ID_GRX188:
case SOC_ID_GRX168:
i->name = SOC_GR9;
i->type = SOC_TYPE_AR9;
break;
case SOC_ID_AMAZON_SE_1:
case SOC_ID_AMAZON_SE_2:
i->name = SOC_AMAZON_SE;
i->type = SOC_TYPE_AMAZON_SE;
#ifdef CONFIG_PCI
panic("ase is only supported for non pci kernels");
#endif
break;
case SOC_ID_VRX282:
case SOC_ID_VRX268:
case SOC_ID_VRX288:
i->name = SOC_VR9;
i->type = SOC_TYPE_VR9_1;
break;
case SOC_ID_GRX268:
case SOC_ID_GRX288:
i->name = SOC_GR9;
i->type = SOC_TYPE_VR9_1;
break;
case SOC_ID_VRX268_2:
case SOC_ID_VRX288_2:
i->name = SOC_VR9;
i->type = SOC_TYPE_VR9_2;
break;
case SOC_ID_GRX282_2:
case SOC_ID_GRX288_2:
i->name = SOC_GR9;
i->type = SOC_TYPE_VR9_2;
default:
unreachable();
break;
}
}
void __init ltq_soc_setup(void)
{
if (ltq_is_ase())
ltq_register_ase_asc();
else
ltq_register_asc(1);
ltq_register_gpio();
ltq_register_wdt();
}

View File

@@ -0,0 +1,283 @@
/*
* 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) 2011 John Crispin <blogic@openwrt.org>
*/
#include <linux/ioport.h>
#include <linux/export.h>
#include <linux/clkdev.h>
#include <lantiq_soc.h>
#include "../clk.h"
#include "../devices.h"
/* clock control register */
#define CGU_IFCCR 0x0018
/* system clock register */
#define CGU_SYS 0x0010
/* pci control register */
#define CGU_PCICR 0x0034
/* ephy configuration register */
#define CGU_EPHY 0x10
/* power control register */
#define PMU_PWDCR 0x1C
/* power status register */
#define PMU_PWDSR 0x20
/* power control register */
#define PMU_PWDCR1 0x24
/* power status register */
#define PMU_PWDSR1 0x28
/* power control register */
#define PWDCR(x) ((x) ? (PMU_PWDCR1) : (PMU_PWDCR))
/* power status register */
#define PWDSR(x) ((x) ? (PMU_PWDSR1) : (PMU_PWDSR))
/* PMU - power management unit */
#define PMU_USB0_P BIT(0)
#define PMU_PCI BIT(4)
#define PMU_DMA BIT(5)
#define PMU_USB0 BIT(6)
#define PMU_EPHY BIT(7) /* ase */
#define PMU_SPI BIT(8)
#define PMU_DFE BIT(9)
#define PMU_EBU BIT(10)
#define PMU_STP BIT(11)
#define PMU_GPT BIT(12)
#define PMU_PPE BIT(13)
#define PMU_AHBS BIT(13) /* vr9 */
#define PMU_FPI BIT(14)
#define PMU_AHBM BIT(15)
#define PMU_PPE_QSB BIT(18)
#define PMU_PPE_SLL01 BIT(19)
#define PMU_PPE_TC BIT(21)
#define PMU_PPE_EMA BIT(22)
#define PMU_PPE_DPLUM BIT(23)
#define PMU_PPE_DPLUS BIT(24)
#define PMU_USB1_P BIT(26)
#define PMU_USB1 BIT(27)
#define PMU_SWITCH BIT(28)
#define PMU_PPE_TOP BIT(29)
#define PMU_GPHY BIT(30)
#define PMU_PCIE_CLK BIT(31)
#define PMU1_PCIE_PHY BIT(0)
#define PMU1_PCIE_CTL BIT(1)
#define PMU1_PCIE_PDI BIT(4)
#define PMU1_PCIE_MSI BIT(5)
#define ltq_pmu_w32(x, y) ltq_w32((x), ltq_pmu_membase + (y))
#define ltq_pmu_r32(x) ltq_r32(ltq_pmu_membase + (x))
static struct resource ltq_cgu_resource =
MEM_RES("cgu", LTQ_CGU_BASE_ADDR, LTQ_CGU_SIZE);
static struct resource ltq_pmu_resource =
MEM_RES("pmu", LTQ_PMU_BASE_ADDR, LTQ_PMU_SIZE);
static struct resource ltq_ebu_resource =
MEM_RES("ebu", LTQ_EBU_BASE_ADDR, LTQ_EBU_SIZE);
void __iomem *ltq_cgu_membase;
void __iomem *ltq_ebu_membase;
static void __iomem *ltq_pmu_membase;
static int ltq_cgu_enable(struct clk *clk)
{
ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | clk->bits, CGU_IFCCR);
return 0;
}
static void ltq_cgu_disable(struct clk *clk)
{
ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~clk->bits, CGU_IFCCR);
}
static int ltq_pmu_enable(struct clk *clk)
{
int err = 1000000;
ltq_pmu_w32(ltq_pmu_r32(PWDCR(clk->module)) & ~clk->bits,
PWDCR(clk->module));
do {} while (--err && (ltq_pmu_r32(PWDSR(clk->module)) & clk->bits));
if (!err)
panic("activating PMU module failed!\n");
return 0;
}
static void ltq_pmu_disable(struct clk *clk)
{
ltq_pmu_w32(ltq_pmu_r32(PWDCR(clk->module)) | clk->bits,
PWDCR(clk->module));
}
static int ltq_pci_enable(struct clk *clk)
{
unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR);
/* set clock bus speed */
if (ltq_is_ar9()) {
ifccr &= ~0x1f00000;
if (clk->rate == CLOCK_33M)
ifccr |= 0xe00000;
else
ifccr |= 0x700000; /* 62.5M */
} else {
ifccr &= ~0xf00000;
if (clk->rate == CLOCK_33M)
ifccr |= 0x800000;
else
ifccr |= 0x400000; /* 62.5M */
}
ltq_cgu_w32(ifccr, CGU_IFCCR);
return 0;
}
static int ltq_pci_ext_enable(struct clk *clk)
{
/* enable external pci clock */
ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~(1 << 16),
CGU_IFCCR);
ltq_cgu_w32((1 << 30), CGU_PCICR);
return 0;
}
static void ltq_pci_ext_disable(struct clk *clk)
{
/* disable external pci clock (internal) */
ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | (1 << 16),
CGU_IFCCR);
ltq_cgu_w32((1 << 31) | (1 << 30), CGU_PCICR);
}
/* manage the clock gates via PMU */
static inline void clkdev_add_pmu(const char *dev, const char *con,
unsigned int module, unsigned int bits)
{
struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
clk->cl.dev_id = dev;
clk->cl.con_id = con;
clk->cl.clk = clk;
clk->enable = ltq_pmu_enable;
clk->disable = ltq_pmu_disable;
clk->module = module;
clk->bits = bits;
clkdev_add(&clk->cl);
}
/* manage the clock generator */
static inline void clkdev_add_cgu(const char *dev, const char *con,
unsigned int bits)
{
struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
clk->cl.dev_id = dev;
clk->cl.con_id = con;
clk->cl.clk = clk;
clk->enable = ltq_cgu_enable;
clk->disable = ltq_cgu_disable;
clk->bits = bits;
clkdev_add(&clk->cl);
}
/* pci needs its own enable function */
static inline void clkdev_add_pci(void)
{
struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
struct clk *clk_ext = kzalloc(sizeof(struct clk), GFP_KERNEL);
/* main pci clock */
clk->cl.dev_id = "ltq_pci";
clk->cl.con_id = NULL;
clk->cl.clk = clk;
clk->rate = CLOCK_33M;
clk->enable = ltq_pci_enable;
clk->disable = ltq_pmu_disable;
clk->module = 0;
clk->bits = PMU_PCI;
clkdev_add(&clk->cl);
/* use internal/external bus clock */
clk_ext->cl.dev_id = "ltq_pci";
clk_ext->cl.con_id = "external";
clk_ext->cl.clk = clk_ext;
clk_ext->enable = ltq_pci_ext_enable;
clk_ext->disable = ltq_pci_ext_disable;
clkdev_add(&clk_ext->cl);
}
void __init ltq_soc_init(void)
{
ltq_pmu_membase = ltq_remap_resource(&ltq_pmu_resource);
if (!ltq_pmu_membase)
panic("Failed to remap pmu memory\n");
ltq_cgu_membase = ltq_remap_resource(&ltq_cgu_resource);
if (!ltq_cgu_membase)
panic("Failed to remap cgu memory\n");
ltq_ebu_membase = ltq_remap_resource(&ltq_ebu_resource);
if (!ltq_ebu_membase)
panic("Failed to remap ebu memory\n");
/* make sure to unprotect the memory region where flash is located */
ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_BUSCON0) & ~EBU_WRDIS, LTQ_EBU_BUSCON0);
/* add our clocks */
clkdev_add_pmu("ltq_fpi", NULL, 0, PMU_FPI);
clkdev_add_pmu("ltq_dma", NULL, 0, PMU_DMA);
clkdev_add_pmu("ltq_stp", NULL, 0, PMU_STP);
clkdev_add_pmu("ltq_spi.0", NULL, 0, PMU_SPI);
clkdev_add_pmu("ltq_gptu", NULL, 0, PMU_GPT);
clkdev_add_pmu("ltq_ebu", NULL, 0, PMU_EBU);
if (!ltq_is_vr9())
clkdev_add_pmu("ltq_etop", NULL, 0, PMU_PPE);
if (!ltq_is_ase())
clkdev_add_pci();
if (ltq_is_ase()) {
if (ltq_cgu_r32(CGU_SYS) & (1 << 5))
clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M);
else
clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M);
clkdev_add_cgu("ltq_etop", "ephycgu", CGU_EPHY),
clkdev_add_pmu("ltq_etop", "ephy", 0, PMU_EPHY);
clkdev_add_pmu("ltq_dsl", NULL, 0,
PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 |
PMU_AHBS | PMU_DFE);
} else if (ltq_is_vr9()) {
clkdev_add_static(ltq_vr9_cpu_hz(), ltq_vr9_fpi_hz(),
ltq_vr9_fpi_hz());
clkdev_add_pmu("ltq_pcie", "phy", 1, PMU1_PCIE_PHY);
clkdev_add_pmu("ltq_pcie", "bus", 0, PMU_PCIE_CLK);
clkdev_add_pmu("ltq_pcie", "msi", 1, PMU1_PCIE_MSI);
clkdev_add_pmu("ltq_pcie", "pdi", 1, PMU1_PCIE_PDI);
clkdev_add_pmu("ltq_pcie", "ctl", 1, PMU1_PCIE_CTL);
clkdev_add_pmu("ltq_pcie", "ahb", 0, PMU_AHBM | PMU_AHBS);
clkdev_add_pmu("usb0", NULL, 0, PMU_USB0 | PMU_USB0_P);
clkdev_add_pmu("usb1", NULL, 0, PMU_USB1 | PMU_USB1_P);
clkdev_add_pmu("ltq_vrx200", NULL, 0,
PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM |
PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 |
PMU_PPE_QSB);
clkdev_add_pmu("ltq_dsl", NULL, 0, PMU_DFE | PMU_AHBS);
} else if (ltq_is_ar9()) {
clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(),
ltq_ar9_fpi_hz());
clkdev_add_pmu("ltq_etop", "switch", 0, PMU_SWITCH);
clkdev_add_pmu("ltq_dsl", NULL, 0,
PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 |
PMU_PPE_QSB | PMU_AHBS | PMU_DFE);
} else {
clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(),
ltq_danube_io_region_clock());
clkdev_add_pmu("ltq_dsl", NULL, 0,
PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 |
PMU_PPE_QSB | PMU_AHBS | PMU_DFE);
}
}

View File

@@ -0,0 +1,846 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/unistd.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <asm/irq.h>
#include <asm/div64.h>
#include "../clk.h"
#include <lantiq_soc.h>
#include <lantiq_irq.h>
#include <lantiq_timer.h>
#define MAX_NUM_OF_32BIT_TIMER_BLOCKS 6
#ifdef TIMER1A
#define FIRST_TIMER TIMER1A
#else
#define FIRST_TIMER 2
#endif
/*
* GPTC divider is set or not.
*/
#define GPTU_CLC_RMC_IS_SET 0
/*
* Timer Interrupt (IRQ)
*/
/* Must be adjusted when ICU driver is available */
#define TIMER_INTERRUPT (INT_NUM_IM3_IRL0 + 22)
/*
* Bits Operation
*/
#define GET_BITS(x, msb, lsb) \
(((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb))
#define SET_BITS(x, msb, lsb, value) \
(((x) & ~(((1 << ((msb) + 1)) - 1) ^ ((1 << (lsb)) - 1))) | \
(((value) & ((1 << (1 + (msb) - (lsb))) - 1)) << (lsb)))
/*
* GPTU Register Mapping
*/
#define LQ_GPTU (KSEG1 + 0x1E100A00)
#define LQ_GPTU_CLC ((volatile u32 *)(LQ_GPTU + 0x0000))
#define LQ_GPTU_ID ((volatile u32 *)(LQ_GPTU + 0x0008))
#define LQ_GPTU_CON(n, X) ((volatile u32 *)(LQ_GPTU + 0x0010 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
#define LQ_GPTU_RUN(n, X) ((volatile u32 *)(LQ_GPTU + 0x0018 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
#define LQ_GPTU_RELOAD(n, X) ((volatile u32 *)(LQ_GPTU + 0x0020 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
#define LQ_GPTU_COUNT(n, X) ((volatile u32 *)(LQ_GPTU + 0x0028 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
#define LQ_GPTU_IRNEN ((volatile u32 *)(LQ_GPTU + 0x00F4))
#define LQ_GPTU_IRNICR ((volatile u32 *)(LQ_GPTU + 0x00F8))
#define LQ_GPTU_IRNCR ((volatile u32 *)(LQ_GPTU + 0x00FC))
/*
* Clock Control Register
*/
#define GPTU_CLC_SMC GET_BITS(*LQ_GPTU_CLC, 23, 16)
#define GPTU_CLC_RMC GET_BITS(*LQ_GPTU_CLC, 15, 8)
#define GPTU_CLC_FSOE (*LQ_GPTU_CLC & (1 << 5))
#define GPTU_CLC_EDIS (*LQ_GPTU_CLC & (1 << 3))
#define GPTU_CLC_SPEN (*LQ_GPTU_CLC & (1 << 2))
#define GPTU_CLC_DISS (*LQ_GPTU_CLC & (1 << 1))
#define GPTU_CLC_DISR (*LQ_GPTU_CLC & (1 << 0))
#define GPTU_CLC_SMC_SET(value) SET_BITS(0, 23, 16, (value))
#define GPTU_CLC_RMC_SET(value) SET_BITS(0, 15, 8, (value))
#define GPTU_CLC_FSOE_SET(value) ((value) ? (1 << 5) : 0)
#define GPTU_CLC_SBWE_SET(value) ((value) ? (1 << 4) : 0)
#define GPTU_CLC_EDIS_SET(value) ((value) ? (1 << 3) : 0)
#define GPTU_CLC_SPEN_SET(value) ((value) ? (1 << 2) : 0)
#define GPTU_CLC_DISR_SET(value) ((value) ? (1 << 0) : 0)
/*
* ID Register
*/
#define GPTU_ID_ID GET_BITS(*LQ_GPTU_ID, 15, 8)
#define GPTU_ID_CFG GET_BITS(*LQ_GPTU_ID, 7, 5)
#define GPTU_ID_REV GET_BITS(*LQ_GPTU_ID, 4, 0)
/*
* Control Register of Timer/Counter nX
* n is the index of block (1 based index)
* X is either A or B
*/
#define GPTU_CON_SRC_EG(n, X) (*LQ_GPTU_CON(n, X) & (1 << 10))
#define GPTU_CON_SRC_EXT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 9))
#define GPTU_CON_SYNC(n, X) (*LQ_GPTU_CON(n, X) & (1 << 8))
#define GPTU_CON_EDGE(n, X) GET_BITS(*LQ_GPTU_CON(n, X), 7, 6)
#define GPTU_CON_INV(n, X) (*LQ_GPTU_CON(n, X) & (1 << 5))
#define GPTU_CON_EXT(n, X) (*LQ_GPTU_CON(n, A) & (1 << 4)) /* Timer/Counter B does not have this bit */
#define GPTU_CON_STP(n, X) (*LQ_GPTU_CON(n, X) & (1 << 3))
#define GPTU_CON_CNT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 2))
#define GPTU_CON_DIR(n, X) (*LQ_GPTU_CON(n, X) & (1 << 1))
#define GPTU_CON_EN(n, X) (*LQ_GPTU_CON(n, X) & (1 << 0))
#define GPTU_CON_SRC_EG_SET(value) ((value) ? 0 : (1 << 10))
#define GPTU_CON_SRC_EXT_SET(value) ((value) ? (1 << 9) : 0)
#define GPTU_CON_SYNC_SET(value) ((value) ? (1 << 8) : 0)
#define GPTU_CON_EDGE_SET(value) SET_BITS(0, 7, 6, (value))
#define GPTU_CON_INV_SET(value) ((value) ? (1 << 5) : 0)
#define GPTU_CON_EXT_SET(value) ((value) ? (1 << 4) : 0)
#define GPTU_CON_STP_SET(value) ((value) ? (1 << 3) : 0)
#define GPTU_CON_CNT_SET(value) ((value) ? (1 << 2) : 0)
#define GPTU_CON_DIR_SET(value) ((value) ? (1 << 1) : 0)
#define GPTU_RUN_RL_SET(value) ((value) ? (1 << 2) : 0)
#define GPTU_RUN_CEN_SET(value) ((value) ? (1 << 1) : 0)
#define GPTU_RUN_SEN_SET(value) ((value) ? (1 << 0) : 0)
#define GPTU_IRNEN_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0)
#define GPTU_IRNCR_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0)
#define TIMER_FLAG_MASK_SIZE(x) (x & 0x0001)
#define TIMER_FLAG_MASK_TYPE(x) (x & 0x0002)
#define TIMER_FLAG_MASK_STOP(x) (x & 0x0004)
#define TIMER_FLAG_MASK_DIR(x) (x & 0x0008)
#define TIMER_FLAG_NONE_EDGE 0x0000
#define TIMER_FLAG_MASK_EDGE(x) (x & 0x0030)
#define TIMER_FLAG_REAL 0x0000
#define TIMER_FLAG_INVERT 0x0040
#define TIMER_FLAG_MASK_INVERT(x) (x & 0x0040)
#define TIMER_FLAG_MASK_TRIGGER(x) (x & 0x0070)
#define TIMER_FLAG_MASK_SYNC(x) (x & 0x0080)
#define TIMER_FLAG_CALLBACK_IN_HB 0x0200
#define TIMER_FLAG_MASK_HANDLE(x) (x & 0x0300)
#define TIMER_FLAG_MASK_SRC(x) (x & 0x1000)
struct timer_dev_timer {
unsigned int f_irq_on;
unsigned int irq;
unsigned int flag;
unsigned long arg1;
unsigned long arg2;
};
struct timer_dev {
struct mutex gptu_mutex;
unsigned int number_of_timers;
unsigned int occupation;
unsigned int f_gptu_on;
struct timer_dev_timer timer[MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2];
};
unsigned long ltq_danube_fpi_bus_clock(int fpi);
unsigned long ltq_vr9_fpi_bus_clock(int fpi);
unsigned int ltq_get_fpi_bus_clock(int fpi) {
if (ltq_is_ase())
return CLOCK_133M;
else if (ltq_is_vr9())
return ltq_vr9_fpi_bus_clock(fpi);
return ltq_danube_fpi_bus_clock(fpi);
}
static long gptu_ioctl(struct file *, unsigned int, unsigned long);
static int gptu_open(struct inode *, struct file *);
static int gptu_release(struct inode *, struct file *);
static struct file_operations gptu_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = gptu_ioctl,
.open = gptu_open,
.release = gptu_release
};
static struct miscdevice gptu_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "gptu",
.fops = &gptu_fops,
};
static struct timer_dev timer_dev;
static irqreturn_t timer_irq_handler(int irq, void *p)
{
unsigned int timer;
unsigned int flag;
struct timer_dev_timer *dev_timer = (struct timer_dev_timer *)p;
timer = irq - TIMER_INTERRUPT;
if (timer < timer_dev.number_of_timers
&& dev_timer == &timer_dev.timer[timer]) {
/* Clear interrupt. */
ltq_w32(1 << timer, LQ_GPTU_IRNCR);
/* Call user hanler or signal. */
flag = dev_timer->flag;
if (!(timer & 0x01)
|| TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) {
/* 16-bit timer or timer A of 32-bit timer */
switch (TIMER_FLAG_MASK_HANDLE(flag)) {
case TIMER_FLAG_CALLBACK_IN_IRQ:
case TIMER_FLAG_CALLBACK_IN_HB:
if (dev_timer->arg1)
(*(timer_callback)dev_timer->arg1)(dev_timer->arg2);
break;
case TIMER_FLAG_SIGNAL:
send_sig((int)dev_timer->arg2, (struct task_struct *)dev_timer->arg1, 0);
break;
}
}
}
return IRQ_HANDLED;
}
static inline void lq_enable_gptu(void)
{
struct clk *clk = clk_get_sys("ltq_gptu", NULL);
clk_enable(clk);
//ltq_pmu_enable(PMU_GPT);
/* Set divider as 1, disable write protection for SPEN, enable module. */
*LQ_GPTU_CLC =
GPTU_CLC_SMC_SET(0x00) |
GPTU_CLC_RMC_SET(0x01) |
GPTU_CLC_FSOE_SET(0) |
GPTU_CLC_SBWE_SET(1) |
GPTU_CLC_EDIS_SET(0) |
GPTU_CLC_SPEN_SET(0) |
GPTU_CLC_DISR_SET(0);
}
static inline void lq_disable_gptu(void)
{
struct clk *clk = clk_get_sys("ltq_gptu", NULL);
ltq_w32(0x00, LQ_GPTU_IRNEN);
ltq_w32(0xfff, LQ_GPTU_IRNCR);
/* Set divider as 0, enable write protection for SPEN, disable module. */
*LQ_GPTU_CLC =
GPTU_CLC_SMC_SET(0x00) |
GPTU_CLC_RMC_SET(0x00) |
GPTU_CLC_FSOE_SET(0) |
GPTU_CLC_SBWE_SET(0) |
GPTU_CLC_EDIS_SET(0) |
GPTU_CLC_SPEN_SET(0) |
GPTU_CLC_DISR_SET(1);
clk_enable(clk);
}
int lq_request_timer(unsigned int timer, unsigned int flag,
unsigned long value, unsigned long arg1, unsigned long arg2)
{
int ret = 0;
unsigned int con_reg, irnen_reg;
int n, X;
if (timer >= FIRST_TIMER + timer_dev.number_of_timers)
return -EINVAL;
printk(KERN_INFO "request_timer(%d, 0x%08X, %lu)...",
timer, flag, value);
if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT)
value &= 0xFFFF;
else
timer &= ~0x01;
mutex_lock(&timer_dev.gptu_mutex);
/*
* Allocate timer.
*/
if (timer < FIRST_TIMER) {
unsigned int mask;
unsigned int shift;
/* This takes care of TIMER1B which is the only choice for Voice TAPI system */
unsigned int offset = TIMER2A;
/*
* Pick up a free timer.
*/
if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) {
mask = 1 << offset;
shift = 1;
} else {
mask = 3 << offset;
shift = 2;
}
for (timer = offset;
timer < offset + timer_dev.number_of_timers;
timer += shift, mask <<= shift)
if (!(timer_dev.occupation & mask)) {
timer_dev.occupation |= mask;
break;
}
if (timer >= offset + timer_dev.number_of_timers) {
printk("failed![%d]\n", __LINE__);
mutex_unlock(&timer_dev.gptu_mutex);
return -EINVAL;
} else
ret = timer;
} else {
register unsigned int mask;
/*
* Check if the requested timer is free.
*/
mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
if ((timer_dev.occupation & mask)) {
printk("failed![%d] mask %#x, timer_dev.occupation %#x\n",
__LINE__, mask, timer_dev.occupation);
mutex_unlock(&timer_dev.gptu_mutex);
return -EBUSY;
} else {
timer_dev.occupation |= mask;
ret = 0;
}
}
/*
* Prepare control register value.
*/
switch (TIMER_FLAG_MASK_EDGE(flag)) {
default:
case TIMER_FLAG_NONE_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x00);
break;
case TIMER_FLAG_RISE_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x01);
break;
case TIMER_FLAG_FALL_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x02);
break;
case TIMER_FLAG_ANY_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x03);
break;
}
if (TIMER_FLAG_MASK_TYPE(flag) == TIMER_FLAG_TIMER)
con_reg |=
TIMER_FLAG_MASK_SRC(flag) ==
TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) :
GPTU_CON_SRC_EXT_SET(0);
else
con_reg |=
TIMER_FLAG_MASK_SRC(flag) ==
TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) :
GPTU_CON_SRC_EG_SET(0);
con_reg |=
TIMER_FLAG_MASK_SYNC(flag) ==
TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) :
GPTU_CON_SYNC_SET(1);
con_reg |=
TIMER_FLAG_MASK_INVERT(flag) ==
TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1);
con_reg |=
TIMER_FLAG_MASK_SIZE(flag) ==
TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) :
GPTU_CON_EXT_SET(1);
con_reg |=
TIMER_FLAG_MASK_STOP(flag) ==
TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0);
con_reg |=
TIMER_FLAG_MASK_TYPE(flag) ==
TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) :
GPTU_CON_CNT_SET(1);
con_reg |=
TIMER_FLAG_MASK_DIR(flag) ==
TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0);
/*
* Fill up running data.
*/
timer_dev.timer[timer - FIRST_TIMER].flag = flag;
timer_dev.timer[timer - FIRST_TIMER].arg1 = arg1;
timer_dev.timer[timer - FIRST_TIMER].arg2 = arg2;
if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
timer_dev.timer[timer - FIRST_TIMER + 1].flag = flag;
/*
* Enable GPTU module.
*/
if (!timer_dev.f_gptu_on) {
lq_enable_gptu();
timer_dev.f_gptu_on = 1;
}
/*
* Enable IRQ.
*/
if (TIMER_FLAG_MASK_HANDLE(flag) != TIMER_FLAG_NO_HANDLE) {
if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL)
timer_dev.timer[timer - FIRST_TIMER].arg1 =
(unsigned long) find_task_by_vpid((int) arg1);
irnen_reg = 1 << (timer - FIRST_TIMER);
if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL
|| (TIMER_FLAG_MASK_HANDLE(flag) ==
TIMER_FLAG_CALLBACK_IN_IRQ
&& timer_dev.timer[timer - FIRST_TIMER].arg1)) {
enable_irq(timer_dev.timer[timer - FIRST_TIMER].irq);
timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 1;
}
} else
irnen_reg = 0;
/*
* Write config register, reload value and enable interrupt.
*/
n = timer >> 1;
X = timer & 0x01;
*LQ_GPTU_CON(n, X) = con_reg;
*LQ_GPTU_RELOAD(n, X) = value;
/* printk("reload value = %d\n", (u32)value); */
*LQ_GPTU_IRNEN |= irnen_reg;
mutex_unlock(&timer_dev.gptu_mutex);
printk("successful!\n");
return ret;
}
EXPORT_SYMBOL(lq_request_timer);
int lq_free_timer(unsigned int timer)
{
unsigned int flag;
unsigned int mask;
int n, X;
if (!timer_dev.f_gptu_on)
return -EINVAL;
if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers)
return -EINVAL;
mutex_lock(&timer_dev.gptu_mutex);
flag = timer_dev.timer[timer - FIRST_TIMER].flag;
if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
timer &= ~0x01;
mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
if (((timer_dev.occupation & mask) ^ mask)) {
mutex_unlock(&timer_dev.gptu_mutex);
return -EINVAL;
}
n = timer >> 1;
X = timer & 0x01;
if (GPTU_CON_EN(n, X))
*LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1);
*LQ_GPTU_IRNEN &= ~GPTU_IRNEN_TC_SET(n, X, 1);
*LQ_GPTU_IRNCR |= GPTU_IRNCR_TC_SET(n, X, 1);
if (timer_dev.timer[timer - FIRST_TIMER].f_irq_on) {
disable_irq(timer_dev.timer[timer - FIRST_TIMER].irq);
timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 0;
}
timer_dev.occupation &= ~mask;
if (!timer_dev.occupation && timer_dev.f_gptu_on) {
lq_disable_gptu();
timer_dev.f_gptu_on = 0;
}
mutex_unlock(&timer_dev.gptu_mutex);
return 0;
}
EXPORT_SYMBOL(lq_free_timer);
int lq_start_timer(unsigned int timer, int is_resume)
{
unsigned int flag;
unsigned int mask;
int n, X;
if (!timer_dev.f_gptu_on)
return -EINVAL;
if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers)
return -EINVAL;
mutex_lock(&timer_dev.gptu_mutex);
flag = timer_dev.timer[timer - FIRST_TIMER].flag;
if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
timer &= ~0x01;
mask = (TIMER_FLAG_MASK_SIZE(flag) ==
TIMER_FLAG_16BIT ? 1 : 3) << timer;
if (((timer_dev.occupation & mask) ^ mask)) {
mutex_unlock(&timer_dev.gptu_mutex);
return -EINVAL;
}
n = timer >> 1;
X = timer & 0x01;
*LQ_GPTU_RUN(n, X) = GPTU_RUN_RL_SET(!is_resume) | GPTU_RUN_SEN_SET(1);
mutex_unlock(&timer_dev.gptu_mutex);
return 0;
}
EXPORT_SYMBOL(lq_start_timer);
int lq_stop_timer(unsigned int timer)
{
unsigned int flag;
unsigned int mask;
int n, X;
if (!timer_dev.f_gptu_on)
return -EINVAL;
if (timer < FIRST_TIMER
|| timer >= FIRST_TIMER + timer_dev.number_of_timers)
return -EINVAL;
mutex_lock(&timer_dev.gptu_mutex);
flag = timer_dev.timer[timer - FIRST_TIMER].flag;
if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
timer &= ~0x01;
mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
if (((timer_dev.occupation & mask) ^ mask)) {
mutex_unlock(&timer_dev.gptu_mutex);
return -EINVAL;
}
n = timer >> 1;
X = timer & 0x01;
*LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1);
mutex_unlock(&timer_dev.gptu_mutex);
return 0;
}
EXPORT_SYMBOL(lq_stop_timer);
int lq_reset_counter_flags(u32 timer, u32 flags)
{
unsigned int oflag;
unsigned int mask, con_reg;
int n, X;
if (!timer_dev.f_gptu_on)
return -EINVAL;
if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers)
return -EINVAL;
mutex_lock(&timer_dev.gptu_mutex);
oflag = timer_dev.timer[timer - FIRST_TIMER].flag;
if (TIMER_FLAG_MASK_SIZE(oflag) != TIMER_FLAG_16BIT)
timer &= ~0x01;
mask = (TIMER_FLAG_MASK_SIZE(oflag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
if (((timer_dev.occupation & mask) ^ mask)) {
mutex_unlock(&timer_dev.gptu_mutex);
return -EINVAL;
}
switch (TIMER_FLAG_MASK_EDGE(flags)) {
default:
case TIMER_FLAG_NONE_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x00);
break;
case TIMER_FLAG_RISE_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x01);
break;
case TIMER_FLAG_FALL_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x02);
break;
case TIMER_FLAG_ANY_EDGE:
con_reg = GPTU_CON_EDGE_SET(0x03);
break;
}
if (TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER)
con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : GPTU_CON_SRC_EXT_SET(0);
else
con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : GPTU_CON_SRC_EG_SET(0);
con_reg |= TIMER_FLAG_MASK_SYNC(flags) == TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : GPTU_CON_SYNC_SET(1);
con_reg |= TIMER_FLAG_MASK_INVERT(flags) == TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1);
con_reg |= TIMER_FLAG_MASK_SIZE(flags) == TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : GPTU_CON_EXT_SET(1);
con_reg |= TIMER_FLAG_MASK_STOP(flags) == TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0);
con_reg |= TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : GPTU_CON_CNT_SET(1);
con_reg |= TIMER_FLAG_MASK_DIR(flags) == TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0);
timer_dev.timer[timer - FIRST_TIMER].flag = flags;
if (TIMER_FLAG_MASK_SIZE(flags) != TIMER_FLAG_16BIT)
timer_dev.timer[timer - FIRST_TIMER + 1].flag = flags;
n = timer >> 1;
X = timer & 0x01;
*LQ_GPTU_CON(n, X) = con_reg;
smp_wmb();
printk(KERN_INFO "[%s]: counter%d oflags %#x, nflags %#x, GPTU_CON %#x\n", __func__, timer, oflag, flags, *LQ_GPTU_CON(n, X));
mutex_unlock(&timer_dev.gptu_mutex);
return 0;
}
EXPORT_SYMBOL(lq_reset_counter_flags);
int lq_get_count_value(unsigned int timer, unsigned long *value)
{
unsigned int flag;
unsigned int mask;
int n, X;
if (!timer_dev.f_gptu_on)
return -EINVAL;
if (timer < FIRST_TIMER
|| timer >= FIRST_TIMER + timer_dev.number_of_timers)
return -EINVAL;
mutex_lock(&timer_dev.gptu_mutex);
flag = timer_dev.timer[timer - FIRST_TIMER].flag;
if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
timer &= ~0x01;
mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
if (((timer_dev.occupation & mask) ^ mask)) {
mutex_unlock(&timer_dev.gptu_mutex);
return -EINVAL;
}
n = timer >> 1;
X = timer & 0x01;
*value = *LQ_GPTU_COUNT(n, X);
mutex_unlock(&timer_dev.gptu_mutex);
return 0;
}
EXPORT_SYMBOL(lq_get_count_value);
u32 lq_cal_divider(unsigned long freq)
{
u64 module_freq, fpi = ltq_get_fpi_bus_clock(2);
u32 clock_divider = 1;
module_freq = fpi * 1000;
do_div(module_freq, clock_divider * freq);
return module_freq;
}
EXPORT_SYMBOL(lq_cal_divider);
int lq_set_timer(unsigned int timer, unsigned int freq, int is_cyclic,
int is_ext_src, unsigned int handle_flag, unsigned long arg1,
unsigned long arg2)
{
unsigned long divider;
unsigned int flag;
divider = lq_cal_divider(freq);
if (divider == 0)
return -EINVAL;
flag = ((divider & ~0xFFFF) ? TIMER_FLAG_32BIT : TIMER_FLAG_16BIT)
| (is_cyclic ? TIMER_FLAG_CYCLIC : TIMER_FLAG_ONCE)
| (is_ext_src ? TIMER_FLAG_EXT_SRC : TIMER_FLAG_INT_SRC)
| TIMER_FLAG_TIMER | TIMER_FLAG_DOWN
| TIMER_FLAG_MASK_HANDLE(handle_flag);
printk(KERN_INFO "lq_set_timer(%d, %d), divider = %lu\n",
timer, freq, divider);
return lq_request_timer(timer, flag, divider, arg1, arg2);
}
EXPORT_SYMBOL(lq_set_timer);
int lq_set_counter(unsigned int timer, unsigned int flag, u32 reload,
unsigned long arg1, unsigned long arg2)
{
printk(KERN_INFO "lq_set_counter(%d, %#x, %d)\n", timer, flag, reload);
return lq_request_timer(timer, flag, reload, arg1, arg2);
}
EXPORT_SYMBOL(lq_set_counter);
static long gptu_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret;
struct gptu_ioctl_param param;
if (!access_ok(VERIFY_READ, arg, sizeof(struct gptu_ioctl_param)))
return -EFAULT;
copy_from_user(&param, (void *) arg, sizeof(param));
if ((((cmd == GPTU_REQUEST_TIMER || cmd == GPTU_SET_TIMER
|| GPTU_SET_COUNTER) && param.timer < 2)
|| cmd == GPTU_GET_COUNT_VALUE || cmd == GPTU_CALCULATE_DIVIDER)
&& !access_ok(VERIFY_WRITE, arg,
sizeof(struct gptu_ioctl_param)))
return -EFAULT;
switch (cmd) {
case GPTU_REQUEST_TIMER:
ret = lq_request_timer(param.timer, param.flag, param.value,
(unsigned long) param.pid,
(unsigned long) param.sig);
if (ret > 0) {
copy_to_user(&((struct gptu_ioctl_param *) arg)->
timer, &ret, sizeof(&ret));
ret = 0;
}
break;
case GPTU_FREE_TIMER:
ret = lq_free_timer(param.timer);
break;
case GPTU_START_TIMER:
ret = lq_start_timer(param.timer, param.flag);
break;
case GPTU_STOP_TIMER:
ret = lq_stop_timer(param.timer);
break;
case GPTU_GET_COUNT_VALUE:
ret = lq_get_count_value(param.timer, &param.value);
if (!ret)
copy_to_user(&((struct gptu_ioctl_param *) arg)->
value, &param.value,
sizeof(param.value));
break;
case GPTU_CALCULATE_DIVIDER:
param.value = lq_cal_divider(param.value);
if (param.value == 0)
ret = -EINVAL;
else {
copy_to_user(&((struct gptu_ioctl_param *) arg)->
value, &param.value,
sizeof(param.value));
ret = 0;
}
break;
case GPTU_SET_TIMER:
ret = lq_set_timer(param.timer, param.value,
TIMER_FLAG_MASK_STOP(param.flag) !=
TIMER_FLAG_ONCE ? 1 : 0,
TIMER_FLAG_MASK_SRC(param.flag) ==
TIMER_FLAG_EXT_SRC ? 1 : 0,
TIMER_FLAG_MASK_HANDLE(param.flag) ==
TIMER_FLAG_SIGNAL ? TIMER_FLAG_SIGNAL :
TIMER_FLAG_NO_HANDLE,
(unsigned long) param.pid,
(unsigned long) param.sig);
if (ret > 0) {
copy_to_user(&((struct gptu_ioctl_param *) arg)->
timer, &ret, sizeof(&ret));
ret = 0;
}
break;
case GPTU_SET_COUNTER:
lq_set_counter(param.timer, param.flag, param.value, 0, 0);
if (ret > 0) {
copy_to_user(&((struct gptu_ioctl_param *) arg)->
timer, &ret, sizeof(&ret));
ret = 0;
}
break;
default:
ret = -ENOTTY;
}
return ret;
}
static int gptu_open(struct inode *inode, struct file *file)
{
return 0;
}
static int gptu_release(struct inode *inode, struct file *file)
{
return 0;
}
int __init lq_gptu_init(void)
{
int ret;
unsigned int i;
ltq_w32(0, LQ_GPTU_IRNEN);
ltq_w32(0xfff, LQ_GPTU_IRNCR);
memset(&timer_dev, 0, sizeof(timer_dev));
mutex_init(&timer_dev.gptu_mutex);
lq_enable_gptu();
timer_dev.number_of_timers = GPTU_ID_CFG * 2;
lq_disable_gptu();
if (timer_dev.number_of_timers > MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2)
timer_dev.number_of_timers = MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2;
printk(KERN_INFO "gptu: totally %d 16-bit timers/counters\n", timer_dev.number_of_timers);
ret = misc_register(&gptu_miscdev);
if (ret) {
printk(KERN_ERR "gptu: can't misc_register, get error %d\n", -ret);
return ret;
} else {
printk(KERN_INFO "gptu: misc_register on minor %d\n", gptu_miscdev.minor);
}
for (i = 0; i < timer_dev.number_of_timers; i++) {
ret = request_irq(TIMER_INTERRUPT + i, timer_irq_handler, IRQF_TIMER, gptu_miscdev.name, &timer_dev.timer[i]);
if (ret) {
for (; i >= 0; i--)
free_irq(TIMER_INTERRUPT + i, &timer_dev.timer[i]);
misc_deregister(&gptu_miscdev);
printk(KERN_ERR "gptu: failed in requesting irq (%d), get error %d\n", i, -ret);
return ret;
} else {
timer_dev.timer[i].irq = TIMER_INTERRUPT + i;
disable_irq(timer_dev.timer[i].irq);
printk(KERN_INFO "gptu: succeeded to request irq %d\n", timer_dev.timer[i].irq);
}
}
return 0;
}
void __exit lq_gptu_exit(void)
{
unsigned int i;
for (i = 0; i < timer_dev.number_of_timers; i++) {
if (timer_dev.timer[i].f_irq_on)
disable_irq(timer_dev.timer[i].irq);
free_irq(timer_dev.timer[i].irq, &timer_dev.timer[i]);
}
lq_disable_gptu();
misc_deregister(&gptu_miscdev);
}
module_init(lq_gptu_init);
module_exit(lq_gptu_exit);