Hi,
This patch series adds support for the Ingenic JZ4740 System-on-a-Chip.
The JZ4740 has a mostly MIPS32 4K compatible core (no on cpu timers) and many on
chip peripherals like RTC, NAND, MMC, OHCI, UDC, ADC, I2C, SPI, AC97, I2S, I2S
Codec, UART and LCD controller.
The JZ4740 is mostly used in eBooks, PMPs and hand-held consoles.
This series contains patches for the Qi Ben NanoNote clamshell device as the
inital supported device.
- Lars
--
Lars-Peter Clausen (26):
MIPS: Add base support for Ingenic JZ4740 System-on-a-Chip
MIPS: jz4740: Add IRQ handler code
MIPS: JZ4740: Add clock API support.
MIPS: JZ4740: Add timer support
MIPS: JZ4740: Add clocksource/clockevent support.
MIPS: JZ4740: Add power-management and system reset support
MIPS: JZ4740: Add setup code
MIPS: JZ4740: Add gpio support
MIPS: JZ4740: Add DMA support.
MIPS: JZ4740: Add PWM support
MIPS: JZ4740: Add serial support
MIPS: JZ4740: Add prom support
MIPS: JZ4740: Add platform devices
MIPS: JZ4740: Add Kbuild files
RTC: Add JZ4740 RTC driver
fbdev: Add JZ4740 framebuffer driver
MTD: Nand: Add JZ4740 NAND driver
MMC: Add JZ4740 mmc driver
USB: Add JZ4740 ohci support
alsa: ASoC: Add JZ4740 codec driver
alsa: ASoC: Add JZ4740 ASoC support
hwmon: Add JZ4740 ADC driver
power: Add JZ4740 battery driver.
MIPS: JZ4740: Add qi_lb60 board support
MIPS: Add defconfig for the qi_lb60 board
alsa: ASoC: JZ4740: Add qi_lb60 board driver
arch/mips/Kconfig | 13 +
arch/mips/Makefile | 6 +
arch/mips/configs/qi_lb60_defconfig | 1456 ++++++++++++++++++++++++++
arch/mips/include/asm/bootinfo.h | 6 +
arch/mips/include/asm/cpu.h | 9 +-
arch/mips/include/asm/mach-jz4740/base.h | 28 +
arch/mips/include/asm/mach-jz4740/clock.h | 28 +
arch/mips/include/asm/mach-jz4740/dma.h | 90 ++
arch/mips/include/asm/mach-jz4740/gpio.h | 398 +++++++
arch/mips/include/asm/mach-jz4740/irq.h | 55 +
arch/mips/include/asm/mach-jz4740/platform.h | 36 +
arch/mips/include/asm/mach-jz4740/timer.h | 22 +
arch/mips/include/asm/mach-jz4740/war.h | 25 +
arch/mips/jz4740/Kconfig | 12 +
arch/mips/jz4740/Makefile | 20 +
arch/mips/jz4740/board-qi_lb60.c | 483 +++++++++
arch/mips/jz4740/clock-debugfs.c | 109 ++
arch/mips/jz4740/clock.c | 920 ++++++++++++++++
arch/mips/jz4740/clock.h | 76 ++
arch/mips/jz4740/dma.c | 289 +++++
arch/mips/jz4740/gpio.c | 605 +++++++++++
arch/mips/jz4740/irq.c | 170 +++
arch/mips/jz4740/irq.h | 21 +
arch/mips/jz4740/platform.c | 288 +++++
arch/mips/jz4740/pm.c | 56 +
arch/mips/jz4740/prom.c | 69 ++
arch/mips/jz4740/pwm.c | 172 +++
arch/mips/jz4740/reset.c | 81 ++
arch/mips/jz4740/reset.h | 7 +
arch/mips/jz4740/serial.c | 33 +
arch/mips/jz4740/serial.h | 21 +
arch/mips/jz4740/setup.c | 33 +
arch/mips/jz4740/time.c | 144 +++
arch/mips/jz4740/timer.c | 48 +
arch/mips/jz4740/timer.h | 136 +++
arch/mips/kernel/cpu-probe.c | 20 +
arch/mips/mm/tlbex.c | 5 +
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/jz4740-adc.c | 423 ++++++++
drivers/mmc/host/Kconfig | 8 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/jz4740_mmc.c | 1000 ++++++++++++++++++
drivers/mtd/nand/Kconfig | 6 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/jz4740_nand.c | 442 ++++++++
drivers/power/Kconfig | 11 +
drivers/power/Makefile | 1 +
drivers/power/jz4740-battery.c | 359 +++++++
drivers/rtc/Kconfig | 11 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-jz4740.c | 344 ++++++
drivers/usb/Kconfig | 1 +
drivers/usb/host/ohci-hcd.c | 5 +
drivers/usb/host/ohci-jz4740.c | 264 +++++
drivers/video/Kconfig | 9 +
drivers/video/Makefile | 1 +
drivers/video/jz4740_fb.c | 818 +++++++++++++++
include/linux/jz4740-adc.h | 25 +
include/linux/jz4740_fb.h | 58 +
include/linux/mmc/jz4740_mmc.h | 15 +
include/linux/mtd/jz4740_nand.h | 34 +
include/linux/power/jz4740-battery.h | 24 +
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/jz4740-codec.c | 502 +++++++++
sound/soc/codecs/jz4740-codec.h | 20 +
sound/soc/jz4740/Kconfig | 21 +
sound/soc/jz4740/Makefile | 13 +
sound/soc/jz4740/jz4740-i2s.c | 568 ++++++++++
sound/soc/jz4740/jz4740-i2s.h | 18 +
sound/soc/jz4740/jz4740-pcm.c | 350 ++++++
sound/soc/jz4740/jz4740-pcm.h | 22 +
sound/soc/jz4740/qi_lb60.c | 175 +++
76 files changed, 11560 insertions(+), 1 deletions(-)
create mode 100644 arch/mips/configs/qi_lb60_defconfig
create mode 100644 arch/mips/include/asm/mach-jz4740/base.h
create mode 100644 arch/mips/include/asm/mach-jz4740/clock.h
create mode 100644 arch/mips/include/asm/mach-jz4740/dma.h
create mode 100644 arch/mips/include/asm/mach-jz4740/gpio.h
create mode 100644 arch/mips/include/asm/mach-jz4740/irq.h
create mode 100644 arch/mips/include/asm/mach-jz4740/platform.h
create mode 100644 arch/mips/include/asm/mach-jz4740/timer.h
create mode 100644 arch/mips/include/asm/mach-jz4740/war.h
create mode 100644 arch/mips/jz4740/Kconfig
create mode 100644 arch/mips/jz4740/Makefile
create mode 100644 arch/mips/jz4740/board-qi_lb60.c
create mode 100644 arch/mips/jz4740/clock-debugfs.c
create mode 100644 arch/mips/jz4740/clock.c
create mode 100644 arch/mips/jz4740/clock.h
create mode 100644 arch/mips/jz4740/dma.c
create mode 100644 arch/mips/jz4740/gpio.c
create mode 100644 arch/mips/jz4740/irq.c
create mode 100644 arch/mips/jz4740/irq.h
create mode 100644 arch/mips/jz4740/platform.c
create mode 100644 arch/mips/jz4740/pm.c
create mode 100644 arch/mips/jz4740/prom.c
create mode 100644 arch/mips/jz4740/pwm.c
create mode 100644 arch/mips/jz4740/reset.c
create mode 100644 arch/mips/jz4740/reset.h
create mode 100644 arch/mips/jz4740/serial.c
create mode 100644 arch/mips/jz4740/serial.h
create mode 100644 arch/mips/jz4740/setup.c
create mode 100644 arch/mips/jz4740/time.c
create mode 100644 arch/mips/jz4740/timer.c
create mode 100644 arch/mips/jz4740/timer.h
create mode 100644 drivers/hwmon/jz4740-adc.c
create mode 100644 drivers/mmc/host/jz4740_mmc.c
create mode 100644 drivers/mtd/nand/jz4740_nand.c
create mode 100644 drivers/power/jz4740-battery.c
create mode 100644 drivers/rtc/rtc-jz4740.c
create mode 100644 drivers/usb/host/ohci-jz4740.c
create mode 100644 drivers/video/jz4740_fb.c
create mode 100644 include/linux/jz4740-adc.h
create mode 100644 include/linux/jz4740_fb.h
create mode 100644 include/linux/mmc/jz4740_mmc.h
create mode 100644 include/linux/mtd/jz4740_nand.h
create mode 100644 include/linux/power/jz4740-battery.h
create mode 100644 sound/soc/codecs/jz4740-codec.c
create mode 100644 sound/soc/codecs/jz4740-codec.h
create mode 100644 sound/soc/jz4740/Kconfig
create mode 100644 sound/soc/jz4740/Makefile
create mode 100644 sound/soc/jz4740/jz4740-i2s.c
create mode 100644 sound/soc/jz4740/jz4740-i2s.h
create mode 100644 sound/soc/jz4740/jz4740-pcm.c
create mode 100644 sound/soc/jz4740/jz4740-pcm.h
create mode 100644 sound/soc/jz4740/qi_lb60.c
This patch adds gpiolib support for JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/include/asm/mach-jz4740/gpio.h | 398 ++++++++++++++++++++
arch/mips/jz4740/gpio.c | 605 ++++++++++++++++++++++++++++++
2 files changed, 1003 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/gpio.h
create mode 100644 arch/mips/jz4740/gpio.c
diff --git a/arch/mips/include/asm/mach-jz4740/gpio.h b/arch/mips/include/asm/mach-jz4740/gpio.h
new file mode 100644
index 0000000..5f175d7
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/gpio.h
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <[email protected]>
+ * JZ7420/JZ4740 GPIO pin definitions
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _JZ_GPIO_H
+#define _JZ_GPIO_H
+
+#include <linux/types.h>
+
+enum jz_gpio_function {
+ JZ_GPIO_FUNC_NONE,
+ JZ_GPIO_FUNC1,
+ JZ_GPIO_FUNC2,
+ JZ_GPIO_FUNC3,
+};
+
+
+/*
+ Usually a driver for a SoC component has to request several gpio pins and
+ configure them as funcion pins.
+ jz_gpio_bulk_request can be used to ease this process.
+ Usually one would do something like:
+
+ const static struct jz_gpio_bulk_request i2c_pins[] = {
+ JZ_GPIO_BULK_PIN(I2C_SDA),
+ JZ_GPIO_BULK_PIN(I2C_SCK),
+ };
+
+ inside the probe function:
+
+ ret = jz_gpio_bulk_request(i2c_pins, ARRAY_SIZE(i2c_pins));
+ if (ret) {
+ ...
+
+ inside the remove function:
+
+ jz_gpio_bulk_free(i2c_pins, ARRAY_SIZE(i2c_pins));
+
+
+*/
+struct jz_gpio_bulk_request {
+ int gpio;
+ const char *name;
+ enum jz_gpio_function function;
+};
+
+#define JZ_GPIO_BULK_PIN(pin) { \
+ .gpio = JZ_GPIO_ ## pin, \
+ .name = #pin, \
+ .function = JZ_GPIO_FUNC_ ## pin \
+}
+
+int jz_gpio_bulk_request(const struct jz_gpio_bulk_request *request, size_t num);
+void jz_gpio_bulk_free(const struct jz_gpio_bulk_request *request, size_t num);
+void jz_gpio_bulk_suspend(const struct jz_gpio_bulk_request *request, size_t num);
+void jz_gpio_bulk_resume(const struct jz_gpio_bulk_request *request, size_t num);
+void jz_gpio_enable_pullup(unsigned gpio);
+void jz_gpio_disable_pullup(unsigned gpio);
+int jz_gpio_set_function(int gpio, enum jz_gpio_function function);
+
+int jz_gpio_port_direction_input(int port, uint32_t mask);
+int jz_gpio_port_direction_output(int port, uint32_t mask);
+void jz_gpio_port_set_value(int port, uint32_t value, uint32_t mask);
+uint32_t jz_gpio_port_get_value(int port, uint32_t mask);
+
+#include <asm/mach-generic/gpio.h>
+
+#define JZ_GPIO_PORTA(x) ((x) + 32 * 0)
+#define JZ_GPIO_PORTB(x) ((x) + 32 * 1)
+#define JZ_GPIO_PORTC(x) ((x) + 32 * 2)
+#define JZ_GPIO_PORTD(x) ((x) + 32 * 3)
+
+/* Port A function pins */
+#define JZ_GPIO_MEM_DATA0 JZ_GPIO_PORTA(0)
+#define JZ_GPIO_MEM_DATA1 JZ_GPIO_PORTA(1)
+#define JZ_GPIO_MEM_DATA2 JZ_GPIO_PORTA(2)
+#define JZ_GPIO_MEM_DATA3 JZ_GPIO_PORTA(3)
+#define JZ_GPIO_MEM_DATA4 JZ_GPIO_PORTA(4)
+#define JZ_GPIO_MEM_DATA5 JZ_GPIO_PORTA(5)
+#define JZ_GPIO_MEM_DATA6 JZ_GPIO_PORTA(6)
+#define JZ_GPIO_MEM_DATA7 JZ_GPIO_PORTA(7)
+#define JZ_GPIO_MEM_DATA8 JZ_GPIO_PORTA(8)
+#define JZ_GPIO_MEM_DATA9 JZ_GPIO_PORTA(9)
+#define JZ_GPIO_MEM_DATA10 JZ_GPIO_PORTA(10)
+#define JZ_GPIO_MEM_DATA11 JZ_GPIO_PORTA(11)
+#define JZ_GPIO_MEM_DATA12 JZ_GPIO_PORTA(12)
+#define JZ_GPIO_MEM_DATA13 JZ_GPIO_PORTA(13)
+#define JZ_GPIO_MEM_DATA14 JZ_GPIO_PORTA(14)
+#define JZ_GPIO_MEM_DATA15 JZ_GPIO_PORTA(15)
+#define JZ_GPIO_MEM_DATA16 JZ_GPIO_PORTA(16)
+#define JZ_GPIO_MEM_DATA17 JZ_GPIO_PORTA(17)
+#define JZ_GPIO_MEM_DATA18 JZ_GPIO_PORTA(18)
+#define JZ_GPIO_MEM_DATA19 JZ_GPIO_PORTA(19)
+#define JZ_GPIO_MEM_DATA20 JZ_GPIO_PORTA(20)
+#define JZ_GPIO_MEM_DATA21 JZ_GPIO_PORTA(21)
+#define JZ_GPIO_MEM_DATA22 JZ_GPIO_PORTA(22)
+#define JZ_GPIO_MEM_DATA23 JZ_GPIO_PORTA(23)
+#define JZ_GPIO_MEM_DATA24 JZ_GPIO_PORTA(24)
+#define JZ_GPIO_MEM_DATA25 JZ_GPIO_PORTA(25)
+#define JZ_GPIO_MEM_DATA26 JZ_GPIO_PORTA(26)
+#define JZ_GPIO_MEM_DATA27 JZ_GPIO_PORTA(27)
+#define JZ_GPIO_MEM_DATA28 JZ_GPIO_PORTA(28)
+#define JZ_GPIO_MEM_DATA29 JZ_GPIO_PORTA(29)
+#define JZ_GPIO_MEM_DATA30 JZ_GPIO_PORTA(30)
+#define JZ_GPIO_MEM_DATA31 JZ_GPIO_PORTA(31)
+
+#define JZ_GPIO_FUNC_MEM_DATA0 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA1 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA2 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA3 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA4 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA5 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA6 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA7 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA8 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA9 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA10 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA11 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA12 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA13 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA14 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA15 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA16 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA17 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA18 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA19 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA20 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA21 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA22 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA23 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA24 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA25 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA26 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA27 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA28 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA29 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA30 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DATA31 JZ_GPIO_FUNC1
+
+/* Port B function pins */
+#define JZ_GPIO_MEM_ADDR0 JZ_GPIO_PORTB(0)
+#define JZ_GPIO_MEM_ADDR1 JZ_GPIO_PORTB(1)
+#define JZ_GPIO_MEM_ADDR2 JZ_GPIO_PORTB(2)
+#define JZ_GPIO_MEM_ADDR3 JZ_GPIO_PORTB(3)
+#define JZ_GPIO_MEM_ADDR4 JZ_GPIO_PORTB(4)
+#define JZ_GPIO_MEM_ADDR5 JZ_GPIO_PORTB(5)
+#define JZ_GPIO_MEM_ADDR6 JZ_GPIO_PORTB(6)
+#define JZ_GPIO_MEM_ADDR7 JZ_GPIO_PORTB(7)
+#define JZ_GPIO_MEM_ADDR8 JZ_GPIO_PORTB(8)
+#define JZ_GPIO_MEM_ADDR9 JZ_GPIO_PORTB(9)
+#define JZ_GPIO_MEM_ADDR10 JZ_GPIO_PORTB(10)
+#define JZ_GPIO_MEM_ADDR11 JZ_GPIO_PORTB(11)
+#define JZ_GPIO_MEM_ADDR12 JZ_GPIO_PORTB(12)
+#define JZ_GPIO_MEM_ADDR13 JZ_GPIO_PORTB(13)
+#define JZ_GPIO_MEM_ADDR14 JZ_GPIO_PORTB(14)
+#define JZ_GPIO_MEM_ADDR15 JZ_GPIO_PORTB(15)
+#define JZ_GPIO_MEM_ADDR16 JZ_GPIO_PORTB(16)
+#define JZ_GPIO_MEM_CLS JZ_GPIO_PORTB(17)
+#define JZ_GPIO_MEM_SPL JZ_GPIO_PORTB(18)
+#define JZ_GPIO_MEM_DCS JZ_GPIO_PORTB(19)
+#define JZ_GPIO_MEM_RAS JZ_GPIO_PORTB(20)
+#define JZ_GPIO_MEM_CAS JZ_GPIO_PORTB(21)
+#define JZ_GPIO_MEM_SDWE JZ_GPIO_PORTB(22)
+#define JZ_GPIO_MEM_CKE JZ_GPIO_PORTB(23)
+#define JZ_GPIO_MEM_CKO JZ_GPIO_PORTB(24)
+#define JZ_GPIO_MEM_CS0 JZ_GPIO_PORTB(25)
+#define JZ_GPIO_MEM_CS1 JZ_GPIO_PORTB(26)
+#define JZ_GPIO_MEM_CS2 JZ_GPIO_PORTB(27)
+#define JZ_GPIO_MEM_CS3 JZ_GPIO_PORTB(28)
+#define JZ_GPIO_MEM_RD JZ_GPIO_PORTB(29)
+#define JZ_GPIO_MEM_WR JZ_GPIO_PORTB(30)
+#define JZ_GPIO_MEM_WE0 JZ_GPIO_PORTB(31)
+
+#define JZ_GPIO_FUNC_MEM_ADDR0 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR1 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR2 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR3 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR4 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR5 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR6 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR7 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR8 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR9 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR10 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR11 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR12 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR13 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR14 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR15 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_ADDR16 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CLS JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_SPL JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_DCS JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_RAS JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CAS JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_SDWE JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CKE JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CKO JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CS0 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CS1 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CS2 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_CS3 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_RD JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_WR JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_WE0 JZ_GPIO_FUNC1
+
+
+#define JZ_GPIO_MEM_ADDR21 JZ_GPIO_PORTB(17)
+#define JZ_GPIO_MEM_ADDR22 JZ_GPIO_PORTB(18)
+
+#define JZ_GPIO_FUNC_MEM_ADDR21 JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_ADDR22 JZ_GPIO_FUNC2
+
+/* Port C function pins */
+#define JZ_GPIO_LCD_DATA0 JZ_GPIO_PORTC(0)
+#define JZ_GPIO_LCD_DATA1 JZ_GPIO_PORTC(1)
+#define JZ_GPIO_LCD_DATA2 JZ_GPIO_PORTC(2)
+#define JZ_GPIO_LCD_DATA3 JZ_GPIO_PORTC(3)
+#define JZ_GPIO_LCD_DATA4 JZ_GPIO_PORTC(4)
+#define JZ_GPIO_LCD_DATA5 JZ_GPIO_PORTC(5)
+#define JZ_GPIO_LCD_DATA6 JZ_GPIO_PORTC(6)
+#define JZ_GPIO_LCD_DATA7 JZ_GPIO_PORTC(7)
+#define JZ_GPIO_LCD_DATA8 JZ_GPIO_PORTC(8)
+#define JZ_GPIO_LCD_DATA9 JZ_GPIO_PORTC(9)
+#define JZ_GPIO_LCD_DATA10 JZ_GPIO_PORTC(10)
+#define JZ_GPIO_LCD_DATA11 JZ_GPIO_PORTC(11)
+#define JZ_GPIO_LCD_DATA12 JZ_GPIO_PORTC(12)
+#define JZ_GPIO_LCD_DATA13 JZ_GPIO_PORTC(13)
+#define JZ_GPIO_LCD_DATA14 JZ_GPIO_PORTC(14)
+#define JZ_GPIO_LCD_DATA15 JZ_GPIO_PORTC(15)
+#define JZ_GPIO_LCD_DATA16 JZ_GPIO_PORTC(16)
+#define JZ_GPIO_LCD_DATA17 JZ_GPIO_PORTC(17)
+#define JZ_GPIO_LCD_PCLK JZ_GPIO_PORTC(18)
+#define JZ_GPIO_LCD_HSYNC JZ_GPIO_PORTC(19)
+#define JZ_GPIO_LCD_VSYNC JZ_GPIO_PORTC(20)
+#define JZ_GPIO_LCD_DE JZ_GPIO_PORTC(21)
+#define JZ_GPIO_LCD_PS JZ_GPIO_PORTC(22)
+#define JZ_GPIO_LCD_REV JZ_GPIO_PORTC(23)
+#define JZ_GPIO_MEM_WE1 JZ_GPIO_PORTC(24)
+#define JZ_GPIO_MEM_WE2 JZ_GPIO_PORTC(25)
+#define JZ_GPIO_MEM_WE3 JZ_GPIO_PORTC(26)
+#define JZ_GPIO_MEM_WAIT JZ_GPIO_PORTC(27)
+#define JZ_GPIO_MEM_FRE JZ_GPIO_PORTC(28)
+#define JZ_GPIO_MEM_FWE JZ_GPIO_PORTC(29)
+
+#define JZ_GPIO_FUNC_LCD_DATA0 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA1 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA2 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA3 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA4 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA5 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA6 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA7 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA8 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA9 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA10 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA11 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA12 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA13 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA14 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA15 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA16 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DATA17 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_PCLK JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_VSYNC JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_HSYNC JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_DE JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_PS JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_LCD_REV JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_WE1 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_WE2 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_WE3 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_WAIT JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_FRE JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MEM_FWE JZ_GPIO_FUNC1
+
+
+#define JZ_GPIO_MEM_ADDR19 JZ_GPIO_PORTB(22)
+#define JZ_GPIO_MEM_ADDR20 JZ_GPIO_PORTB(23)
+
+#define JZ_GPIO_FUNC_MEM_ADDR19 JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_ADDR20 JZ_GPIO_FUNC2
+
+/* Port D function pins */
+#define JZ_GPIO_CIM_DATA0 JZ_GPIO_PORTD(0)
+#define JZ_GPIO_CIM_DATA1 JZ_GPIO_PORTD(1)
+#define JZ_GPIO_CIM_DATA2 JZ_GPIO_PORTD(2)
+#define JZ_GPIO_CIM_DATA3 JZ_GPIO_PORTD(3)
+#define JZ_GPIO_CIM_DATA4 JZ_GPIO_PORTD(4)
+#define JZ_GPIO_CIM_DATA5 JZ_GPIO_PORTD(5)
+#define JZ_GPIO_CIM_DATA6 JZ_GPIO_PORTD(6)
+#define JZ_GPIO_CIM_DATA7 JZ_GPIO_PORTD(7)
+#define JZ_GPIO_MSC_CMD JZ_GPIO_PORTD(8)
+#define JZ_GPIO_MSC_CLK JZ_GPIO_PORTD(9)
+#define JZ_GPIO_MSC_DATA0 JZ_GPIO_PORTD(10)
+#define JZ_GPIO_MSC_DATA1 JZ_GPIO_PORTD(11)
+#define JZ_GPIO_MSC_DATA2 JZ_GPIO_PORTD(12)
+#define JZ_GPIO_MSC_DATA3 JZ_GPIO_PORTD(13)
+#define JZ_GPIO_CIM_MCLK JZ_GPIO_PORTD(14)
+#define JZ_GPIO_CIM_PCLK JZ_GPIO_PORTD(15)
+#define JZ_GPIO_CIM_VSYNC JZ_GPIO_PORTD(16)
+#define JZ_GPIO_CIM_HSYNC JZ_GPIO_PORTD(17)
+#define JZ_GPIO_SPI_CLK JZ_GPIO_PORTD(18)
+#define JZ_GPIO_SPI_CE0 JZ_GPIO_PORTD(19)
+#define JZ_GPIO_SPI_DT JZ_GPIO_PORTD(20)
+#define JZ_GPIO_SPI_DR JZ_GPIO_PORTD(21)
+#define JZ_GPIO_SPI_CE1 JZ_GPIO_PORTD(22)
+#define JZ_GPIO_PWM0 JZ_GPIO_PORTD(23)
+#define JZ_GPIO_PWM1 JZ_GPIO_PORTD(24)
+#define JZ_GPIO_PWM2 JZ_GPIO_PORTD(25)
+#define JZ_GPIO_PWM3 JZ_GPIO_PORTD(26)
+#define JZ_GPIO_PWM4 JZ_GPIO_PORTD(27)
+#define JZ_GPIO_PWM5 JZ_GPIO_PORTD(28)
+#define JZ_GPIO_PWM6 JZ_GPIO_PORTD(30)
+#define JZ_GPIO_PWM7 JZ_GPIO_PORTD(31)
+
+#define JZ_GPIO_FUNC_CIM_DATA JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_CIM_DATA0 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_CIM_DATA1 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_CIM_DATA2 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_CIM_DATA3 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_CIM_DATA4 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_CIM_DATA5 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_CIM_DATA6 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_CIM_DATA7 JZ_GPIO_FUNC_CIM_DATA
+#define JZ_GPIO_FUNC_MSC_CMD JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MSC_CLK JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MSC_DATA JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_MSC_DATA0 JZ_GPIO_FUNC_MSC_DATA
+#define JZ_GPIO_FUNC_MSC_DATA1 JZ_GPIO_FUNC_MSC_DATA
+#define JZ_GPIO_FUNC_MSC_DATA2 JZ_GPIO_FUNC_MSC_DATA
+#define JZ_GPIO_FUNC_MSC_DATA3 JZ_GPIO_FUNC_MSC_DATA
+#define JZ_GPIO_FUNC_CIM_MCLK JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_CIM_PCLK JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_CIM_VSYNC JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_CIM_HSYNC JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_SPI_CLK JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_SPI_CE0 JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_SPI_DT JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_SPI_DR JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_SPI_CE1 JZ_GPIO_FUNC1
+
+#define JZ_GPIO_FUNC_PWM JZ_GPIO_FUNC1
+#define JZ_GPIO_FUNC_PWM0 JZ_GPIO_FUNC_PWM
+#define JZ_GPIO_FUNC_PWM1 JZ_GPIO_FUNC_PWM
+#define JZ_GPIO_FUNC_PWM2 JZ_GPIO_FUNC_PWM
+#define JZ_GPIO_FUNC_PWM3 JZ_GPIO_FUNC_PWM
+#define JZ_GPIO_FUNC_PWM4 JZ_GPIO_FUNC_PWM
+#define JZ_GPIO_FUNC_PWM5 JZ_GPIO_FUNC_PWM
+#define JZ_GPIO_FUNC_PWM6 JZ_GPIO_FUNC_PWM
+#define JZ_GPIO_FUNC_PWM7 JZ_GPIO_FUNC_PWM
+
+#define JZ_GPIO_MEM_SCLK_RSTN JZ_GPIO_PORTD(18)
+#define JZ_GPIO_MEM_BCLK JZ_GPIO_PORTD(19)
+#define JZ_GPIO_MEM_SDATO JZ_GPIO_PORTD(20)
+#define JZ_GPIO_MEM_SDATI JZ_GPIO_PORTD(21)
+#define JZ_GPIO_MEM_SYNC JZ_GPIO_PORTD(22)
+#define JZ_GPIO_I2C_SDA JZ_GPIO_PORTD(23)
+#define JZ_GPIO_I2C_SCK JZ_GPIO_PORTD(24)
+#define JZ_GPIO_UART0_TXD JZ_GPIO_PORTD(25)
+#define JZ_GPIO_UART0_RXD JZ_GPIO_PORTD(26)
+#define JZ_GPIO_MEM_ADDR17 JZ_GPIO_PORTD(27)
+#define JZ_GPIO_MEM_ADDR18 JZ_GPIO_PORTD(28)
+#define JZ_GPIO_UART0_CTS JZ_GPIO_PORTD(30)
+#define JZ_GPIO_UART0_RTS JZ_GPIO_PORTD(31)
+
+#define JZ_GPIO_FUNC_MEM_SCLK_RSTN JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_BCLK JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_SDATO JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_SDATI JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_SYNC JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_I2C_SDA JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_I2C_SCK JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_UART0_TXD JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_UART0_RXD JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_ADDR17 JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_MEM_ADDR18 JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_UART0_CTS JZ_GPIO_FUNC2
+#define JZ_GPIO_FUNC_UART0_RTS JZ_GPIO_FUNC2
+
+#define JZ_GPIO_UART1_RXD JZ_GPIO_PORTD(30)
+#define JZ_GPIO_UART1_TXD JZ_GPIO_PORTD(31)
+
+#define JZ_GPIO_FUNC_UART1_RXD JZ_GPIO_FUNC3
+#define JZ_GPIO_FUNC_UART1_TXD JZ_GPIO_FUNC3
+
+#endif
diff --git a/arch/mips/jz4740/gpio.c b/arch/mips/jz4740/gpio.c
new file mode 100644
index 0000000..8ef08da
--- /dev/null
+++ b/arch/mips/jz4740/gpio.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform GPIO support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/spinlock.h>
+#include <linux/sysdev.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/mach-jz4740/base.h>
+
+#define JZ4740_GPIO_BASE_A (32*0)
+#define JZ4740_GPIO_BASE_B (32*1)
+#define JZ4740_GPIO_BASE_C (32*2)
+#define JZ4740_GPIO_BASE_D (32*3)
+
+#define JZ4740_GPIO_NUM_A 32
+#define JZ4740_GPIO_NUM_B 32
+#define JZ4740_GPIO_NUM_C 31
+#define JZ4740_GPIO_NUM_D 32
+
+#define JZ4740_IRQ_GPIO_BASE_A (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_A)
+#define JZ4740_IRQ_GPIO_BASE_B (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_B)
+#define JZ4740_IRQ_GPIO_BASE_C (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_C)
+#define JZ4740_IRQ_GPIO_BASE_D (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_D)
+
+#define JZ_REG_GPIO_PIN 0x00
+#define JZ_REG_GPIO_DATA 0x10
+#define JZ_REG_GPIO_DATA_SET 0x14
+#define JZ_REG_GPIO_DATA_CLEAR 0x18
+#define JZ_REG_GPIO_MASK 0x20
+#define JZ_REG_GPIO_MASK_SET 0x24
+#define JZ_REG_GPIO_MASK_CLEAR 0x28
+#define JZ_REG_GPIO_PULL 0x30
+#define JZ_REG_GPIO_PULL_SET 0x34
+#define JZ_REG_GPIO_PULL_CLEAR 0x38
+#define JZ_REG_GPIO_FUNC 0x40
+#define JZ_REG_GPIO_FUNC_SET 0x44
+#define JZ_REG_GPIO_FUNC_CLEAR 0x48
+#define JZ_REG_GPIO_SELECT 0x50
+#define JZ_REG_GPIO_SELECT_SET 0x54
+#define JZ_REG_GPIO_SELECT_CLEAR 0x58
+#define JZ_REG_GPIO_DIRECTION 0x60
+#define JZ_REG_GPIO_DIRECTION_SET 0x64
+#define JZ_REG_GPIO_DIRECTION_CLEAR 0x68
+#define JZ_REG_GPIO_TRIGGER 0x70
+#define JZ_REG_GPIO_TRIGGER_SET 0x74
+#define JZ_REG_GPIO_TRIGGER_CLEAR 0x78
+#define JZ_REG_GPIO_FLAG 0x80
+#define JZ_REG_GPIO_FLAG_CLEAR 0x14
+
+
+#define GPIO_TO_BIT(gpio) BIT(gpio & 0x1f)
+#define GPIO_TO_REG(gpio, reg) (gpio_to_jz_gpio_chip(gpio)->base + (reg))
+#define CHIP_TO_REG(chip, reg) (gpio_chip_to_jz_gpio_chip(chip)->base + (reg))
+
+struct jz_gpio_chip {
+ unsigned int irq;
+ unsigned int irq_base;
+ uint32_t wakeup;
+ uint32_t suspend_mask;
+ uint32_t edge_trigger_both;
+
+ void __iomem *base;
+
+ spinlock_t lock;
+
+ struct gpio_chip gpio_chip;
+ struct irq_chip irq_chip;
+ struct sys_device sysdev;
+};
+
+
+static struct jz_gpio_chip jz4740_gpio_chips[];
+
+static inline struct jz_gpio_chip *gpio_to_jz_gpio_chip(unsigned int gpio)
+{
+ return &jz4740_gpio_chips[gpio >> 5];
+}
+
+static inline struct jz_gpio_chip *gpio_chip_to_jz_gpio_chip(struct gpio_chip *gpio_chip)
+{
+ return container_of(gpio_chip, struct jz_gpio_chip, gpio_chip);
+}
+
+static inline struct jz_gpio_chip *irq_to_jz_gpio_chip(unsigned int irq)
+{
+ return get_irq_chip_data(irq);
+}
+
+static inline void jz_gpio_write_bit(unsigned int gpio, unsigned int reg)
+{
+ writel(GPIO_TO_BIT(gpio), GPIO_TO_REG(gpio, reg));
+}
+
+int jz_gpio_set_function(int gpio, enum jz_gpio_function function)
+{
+ if (function == JZ_GPIO_FUNC_NONE) {
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_FUNC_CLEAR);
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_SELECT_CLEAR);
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_TRIGGER_CLEAR);
+ } else {
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_FUNC_SET);
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_TRIGGER_CLEAR);
+ switch (function) {
+ case JZ_GPIO_FUNC1:
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_SELECT_CLEAR);
+ break;
+ case JZ_GPIO_FUNC3:
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_TRIGGER_SET);
+ case JZ_GPIO_FUNC2: /* Falltrough */
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_SELECT_SET);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(jz_gpio_set_function);
+
+int jz_gpio_bulk_request(const struct jz_gpio_bulk_request *request, size_t num)
+{
+ size_t i;
+ int ret;
+
+ for (i = 0; i < num; ++i, ++request) {
+ ret = gpio_request(request->gpio, request->name);
+ if (ret)
+ goto err;
+ jz_gpio_set_function(request->gpio, request->function);
+ }
+
+ return 0;
+
+err:
+ for (--request; i > 0; --i, --request) {
+ gpio_free(request->gpio);
+ jz_gpio_set_function(request->gpio, JZ_GPIO_FUNC_NONE);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(jz_gpio_bulk_request);
+
+void jz_gpio_bulk_free(const struct jz_gpio_bulk_request *request, size_t num)
+{
+ size_t i;
+
+ for (i = 0; i < num; ++i, ++request) {
+ gpio_free(request->gpio);
+ jz_gpio_set_function(request->gpio, JZ_GPIO_FUNC_NONE);
+ }
+
+}
+EXPORT_SYMBOL_GPL(jz_gpio_bulk_free);
+
+void jz_gpio_bulk_suspend(const struct jz_gpio_bulk_request *request, size_t num)
+{
+ size_t i;
+
+ for (i = 0; i < num; ++i, ++request) {
+ jz_gpio_set_function(request->gpio, JZ_GPIO_FUNC_NONE);
+ jz_gpio_write_bit(request->gpio, JZ_REG_GPIO_DIRECTION_CLEAR);
+ jz_gpio_write_bit(request->gpio, JZ_REG_GPIO_PULL_SET);
+ }
+}
+EXPORT_SYMBOL_GPL(jz_gpio_bulk_suspend);
+
+void jz_gpio_bulk_resume(const struct jz_gpio_bulk_request *request, size_t num)
+{
+ size_t i;
+
+ for (i = 0; i < num; ++i, ++request)
+ jz_gpio_set_function(request->gpio, request->function);
+}
+EXPORT_SYMBOL_GPL(jz_gpio_bulk_resume);
+
+void jz_gpio_enable_pullup(unsigned gpio)
+{
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_PULL_CLEAR);
+}
+EXPORT_SYMBOL_GPL(jz_gpio_enable_pullup);
+
+void jz_gpio_disable_pullup(unsigned gpio)
+{
+ jz_gpio_write_bit(gpio, JZ_REG_GPIO_PULL_SET);
+}
+EXPORT_SYMBOL_GPL(jz_gpio_disable_pullup);
+
+static int jz_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ return !!(readl(CHIP_TO_REG(chip, JZ_REG_GPIO_PIN)) & BIT(gpio));
+}
+
+static void jz_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ uint32_t __iomem *reg = CHIP_TO_REG(chip, JZ_REG_GPIO_DATA_SET);
+ reg += !value;
+ writel(BIT(gpio), reg);
+}
+
+static int jz_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ writel(BIT(gpio), CHIP_TO_REG(chip, JZ_REG_GPIO_DIRECTION_SET));
+ jz_gpio_set_value(chip, gpio, value);
+
+ return 0;
+}
+
+static int jz_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ writel(BIT(gpio), CHIP_TO_REG(chip, JZ_REG_GPIO_DIRECTION_CLEAR));
+
+ return 0;
+}
+
+int jz_gpio_port_direction_input(int port, uint32_t mask)
+{
+ writel(mask, GPIO_TO_REG(port, JZ_REG_GPIO_DIRECTION_CLEAR));
+
+ return 0;
+}
+EXPORT_SYMBOL(jz_gpio_port_direction_input);
+
+int jz_gpio_port_direction_output(int port, uint32_t mask)
+{
+ writel(mask, GPIO_TO_REG(port, JZ_REG_GPIO_DIRECTION_SET));
+
+ return 0;
+}
+EXPORT_SYMBOL(jz_gpio_port_direction_output);
+
+void jz_gpio_port_set_value(int port, uint32_t value, uint32_t mask)
+{
+ writel(~value & mask, GPIO_TO_REG(port, JZ_REG_GPIO_DATA_CLEAR));
+ writel(value & mask, GPIO_TO_REG(port, JZ_REG_GPIO_DATA_SET));
+}
+EXPORT_SYMBOL(jz_gpio_port_set_value);
+
+uint32_t jz_gpio_port_get_value(int port, uint32_t mask)
+{
+ uint32_t value = readl(GPIO_TO_REG(port, JZ_REG_GPIO_PIN));
+
+ return value & mask;
+}
+EXPORT_SYMBOL(jz_gpio_port_get_value);
+
+int gpio_to_irq(unsigned gpio)
+{
+ return JZ4740_IRQ_GPIO(0) + gpio;
+}
+EXPORT_SYMBOL_GPL(gpio_to_irq);
+
+int irq_to_gpio(unsigned irq)
+{
+ return irq - JZ4740_IRQ_GPIO(0);
+}
+EXPORT_SYMBOL_GPL(irq_to_gpio);
+
+#define IRQ_TO_BIT(irq) BIT(irq_to_gpio(irq) & 0x1f)
+
+static void jz_gpio_check_trigger_both(struct jz_gpio_chip *chip, unsigned int irq)
+{
+ uint32_t value;
+ void __iomem *reg;
+ uint32_t mask = IRQ_TO_BIT(irq);
+
+ if (!(chip->edge_trigger_both & mask))
+ return;
+
+ reg = chip->base;
+
+ value = readl(chip->base + JZ_REG_GPIO_PIN);
+ if (value & mask)
+ reg += JZ_REG_GPIO_DIRECTION_CLEAR;
+ else
+ reg += JZ_REG_GPIO_DIRECTION_SET;
+
+ writel(mask, reg);
+}
+
+static void jz_gpio_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+ uint32_t flag;
+ unsigned int gpio_irq;
+ unsigned int gpio_bank;
+ struct jz_gpio_chip *chip = get_irq_desc_data(desc);
+
+ gpio_bank = JZ4740_IRQ_GPIO0 - irq;
+
+ flag = readl(chip->base + JZ_REG_GPIO_FLAG);
+
+ gpio_irq = ffs(flag) - 1;
+
+ jz_gpio_check_trigger_both(chip, irq);
+
+ gpio_irq += (gpio_bank << 5) + JZ4740_IRQ_GPIO(0);
+
+ generic_handle_irq(gpio_irq);
+};
+
+static inline void jz_gpio_set_irq_bit(unsigned int irq, unsigned int reg)
+{
+ struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq);
+ writel(IRQ_TO_BIT(irq), chip->base + reg);
+}
+
+static void jz_gpio_irq_mask(unsigned int irq)
+{
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_MASK_SET);
+};
+
+static void jz_gpio_irq_unmask(unsigned int irq)
+{
+ struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq);
+
+ jz_gpio_check_trigger_both(chip, irq);
+
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_MASK_CLEAR);
+};
+
+
+/* TODO: Check if function is gpio */
+static unsigned int jz_gpio_irq_startup(unsigned int irq)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_SELECT_SET);
+
+ desc->status &= ~IRQ_MASKED;
+ jz_gpio_irq_unmask(irq);
+
+ return 0;
+}
+
+static void jz_gpio_irq_shutdown(unsigned int irq)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ jz_gpio_irq_mask(irq);
+ desc->status |= IRQ_MASKED;
+
+ /* Set direction to input */
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_CLEAR);
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_SELECT_CLEAR);
+}
+
+static void jz_gpio_irq_ack(unsigned int irq)
+{
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_FLAG_CLEAR);
+};
+
+static int jz_gpio_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+ struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq);
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ jz_gpio_irq_mask(irq);
+
+ if (flow_type == IRQ_TYPE_EDGE_BOTH) {
+ uint32_t value = readl(chip->base + JZ_REG_GPIO_PIN);
+ if (value & IRQ_TO_BIT(irq))
+ flow_type = IRQ_TYPE_EDGE_FALLING;
+ else
+ flow_type = IRQ_TYPE_EDGE_RISING;
+ chip->edge_trigger_both |= IRQ_TO_BIT(irq);
+ } else {
+ chip->edge_trigger_both &= ~IRQ_TO_BIT(irq);
+ }
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_SET);
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_SET);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_CLEAR);
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_SET);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_SET);
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_CLEAR);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_CLEAR);
+ jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_CLEAR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!(desc->status & IRQ_MASKED))
+ jz_gpio_irq_unmask(irq);
+
+ return 0;
+}
+
+static int jz_gpio_irq_set_wake(unsigned int irq, unsigned int on)
+{
+ struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq);
+ spin_lock(&chip->lock);
+ if (on)
+ chip->wakeup |= IRQ_TO_BIT(irq);
+ else
+ chip->wakeup &= ~IRQ_TO_BIT(irq);
+ spin_unlock(&chip->lock);
+
+ set_irq_wake(chip->irq, on);
+ return 0;
+}
+
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
+#define JZ4740_GPIO_CHIP(_bank) { \
+ .irq_base = JZ4740_IRQ_GPIO_BASE_ ## _bank, \
+ .gpio_chip = { \
+ .label = "Bank " # _bank, \
+ .owner = THIS_MODULE, \
+ .set = jz_gpio_set_value, \
+ .get = jz_gpio_get_value, \
+ .direction_output = jz_gpio_direction_output, \
+ .direction_input = jz_gpio_direction_input, \
+ .base = JZ4740_GPIO_BASE_ ## _bank, \
+ .ngpio = JZ4740_GPIO_NUM_ ## _bank, \
+ }, \
+ .irq_chip = { \
+ .name = "GPIO Bank " # _bank, \
+ .mask = jz_gpio_irq_mask, \
+ .unmask = jz_gpio_irq_unmask, \
+ .ack = jz_gpio_irq_ack, \
+ .startup = jz_gpio_irq_startup, \
+ .shutdown = jz_gpio_irq_shutdown, \
+ .set_type = jz_gpio_irq_set_type, \
+ .set_wake = jz_gpio_irq_set_wake, \
+ }, \
+}
+
+static struct jz_gpio_chip jz4740_gpio_chips[] = {
+ JZ4740_GPIO_CHIP(A),
+ JZ4740_GPIO_CHIP(B),
+ JZ4740_GPIO_CHIP(C),
+ JZ4740_GPIO_CHIP(D),
+};
+
+static inline struct jz_gpio_chip *sysdev_to_chip(struct sys_device *dev)
+{
+ return container_of(dev, struct jz_gpio_chip, sysdev);
+}
+
+static int jz4740_gpio_suspend(struct sys_device *dev, pm_message_t state)
+{
+ struct jz_gpio_chip *chip = sysdev_to_chip(dev);
+
+ chip->suspend_mask = readl(chip->base + JZ_REG_GPIO_MASK);
+ writel(~(chip->wakeup), chip->base + JZ_REG_GPIO_MASK_SET);
+ writel(chip->wakeup, chip->base + JZ_REG_GPIO_MASK_CLEAR);
+
+ return 0;
+}
+
+static int jz4740_gpio_resume(struct sys_device *dev)
+{
+ struct jz_gpio_chip *chip = sysdev_to_chip(dev);
+ uint32_t mask = chip->suspend_mask;
+
+ writel(~mask, chip->base + JZ_REG_GPIO_MASK_CLEAR);
+ writel(mask, chip->base + JZ_REG_GPIO_MASK_SET);
+
+ return 0;
+}
+
+static struct sysdev_class jz4740_gpio_sysdev_class = {
+ .name = "gpio",
+ .suspend = jz4740_gpio_suspend,
+ .resume = jz4740_gpio_resume,
+};
+
+static int jz4740_gpio_chip_init(struct jz_gpio_chip *chip, unsigned int id)
+{
+ int ret, irq;
+
+ chip->sysdev.id = id;
+ chip->sysdev.cls = &jz4740_gpio_sysdev_class;
+ ret = sysdev_register(&chip->sysdev);
+
+ if (ret)
+ return ret;
+
+ spin_lock_init(&chip->lock);
+
+ chip->base = ioremap(CPHYSADDR(JZ4740_GPIO_BASE_ADDR) + (id * 0x100), 0x100);
+
+ gpiochip_add(&chip->gpio_chip);
+
+ chip->irq = JZ4740_IRQ_INTC_GPIO(id);
+ set_irq_data(chip->irq, chip);
+ set_irq_chained_handler(chip->irq, jz_gpio_irq_demux_handler);
+
+ for (irq = chip->irq_base; irq < chip->irq_base + chip->gpio_chip.ngpio; ++irq) {
+ lockdep_set_class(&irq_desc[irq].lock, &gpio_lock_class);
+ set_irq_chip_and_handler(irq, &chip->irq_chip, handle_level_irq);
+ set_irq_chip_data(irq, chip);
+ }
+
+ return 0;
+}
+
+static int __init jz4740_gpio_init(void)
+{
+ unsigned int i;
+ int ret;
+
+ ret = sysdev_class_register(&jz4740_gpio_sysdev_class);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(jz4740_gpio_chips); ++i)
+ jz4740_gpio_chip_init(&jz4740_gpio_chips[i], i);
+
+ printk(KERN_INFO "JZ4740 GPIO initalized\n");
+
+ return 0;
+}
+arch_initcall(jz4740_gpio_init);
+
+#ifdef CONFIG_DEBUG_FS
+
+static inline void gpio_seq_reg(struct seq_file *s, struct jz_gpio_chip *chip,
+ const char *name, unsigned int reg)
+{
+ seq_printf(s, "\t%s: %08x\n", name, readl(chip->base + reg));
+}
+
+
+static int gpio_regs_show(struct seq_file *s, void *unused)
+{
+ struct jz_gpio_chip *chip = jz4740_gpio_chips;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(jz4740_gpio_chips); ++i, ++chip) {
+ seq_printf(s, "==GPIO %d==\n", i);
+ gpio_seq_reg(s, chip, "Pin", JZ_REG_GPIO_PIN);
+ gpio_seq_reg(s, chip, "Data", JZ_REG_GPIO_DATA);
+ gpio_seq_reg(s, chip, "Mask", JZ_REG_GPIO_MASK);
+ gpio_seq_reg(s, chip, "Pull", JZ_REG_GPIO_PULL);
+ gpio_seq_reg(s, chip, "Func", JZ_REG_GPIO_FUNC);
+ gpio_seq_reg(s, chip, "Select", JZ_REG_GPIO_SELECT);
+ gpio_seq_reg(s, chip, "Direction", JZ_REG_GPIO_DIRECTION);
+ gpio_seq_reg(s, chip, "Trigger", JZ_REG_GPIO_TRIGGER);
+ gpio_seq_reg(s, chip, "Flag", JZ_REG_GPIO_FLAG);
+ }
+
+ return 0;
+}
+
+static int gpio_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, gpio_regs_show, NULL);
+}
+
+static const struct file_operations gpio_regs_operations = {
+ .open = gpio_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init gpio_debugfs_init(void)
+{
+ (void) debugfs_create_file("jz_regs_gpio", S_IFREG | S_IRUGO,
+ NULL, NULL, &gpio_regs_operations);
+ return 0;
+}
+subsys_initcall(gpio_debugfs_init);
+
+#endif
--
1.5.6.5
This patch adds support for suspend/resume and poweroff/reboot on a JZ4740 SoC.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/jz4740/pm.c | 56 +++++++++++++++++++++++++++++++
arch/mips/jz4740/reset.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++
arch/mips/jz4740/reset.h | 7 ++++
3 files changed, 144 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/pm.c
create mode 100644 arch/mips/jz4740/reset.c
create mode 100644 arch/mips/jz4740/reset.h
diff --git a/arch/mips/jz4740/pm.c b/arch/mips/jz4740/pm.c
new file mode 100644
index 0000000..3b78ced
--- /dev/null
+++ b/arch/mips/jz4740/pm.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC power management support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+
+#include <asm/mach-jz4740/clock.h>
+
+#include "clock.h"
+#include "irq.h"
+
+static int jz4740_pm_enter(suspend_state_t state)
+{
+ jz4740_intc_suspend();
+ jz4740_clock_suspend();
+
+ jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_SLEEP);
+
+ __asm__(".set\tmips3\n\t"
+ "wait\n\t"
+ ".set\tmips0");
+
+ jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_IDLE);
+
+ jz4740_clock_resume();
+ jz4740_intc_resume();
+
+ return 0;
+}
+
+static struct platform_suspend_ops jz4740_pm_ops = {
+ .valid = suspend_valid_only_mem,
+ .enter = jz4740_pm_enter,
+};
+
+static int __init jz4740_pm_init(void)
+{
+ suspend_set_ops(&jz4740_pm_ops);
+ return 0;
+
+}
+late_initcall(jz4740_pm_init);
diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c
new file mode 100644
index 0000000..448a7da
--- /dev/null
+++ b/arch/mips/jz4740/reset.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/pm.h>
+
+#include <linux/delay.h>
+
+#include <asm/reboot.h>
+
+#include <asm/mach-jz4740/base.h>
+#include <asm/mach-jz4740/timer.h>
+
+static void jz4740_halt(void)
+{
+ while (1) {
+ __asm__(".set push;\n"
+ ".set mips3;\n"
+ "wait;\n"
+ ".set pop;\n"
+ );
+ }
+}
+
+#define JZ_REG_WDT_DATA 0x00
+#define JZ_REG_WDT_COUNTER_ENABLE 0x04
+#define JZ_REG_WDT_COUNTER 0x08
+#define JZ_REG_WDT_CTRL 0x0c
+
+static void jz4740_restart(char *command)
+{
+ void __iomem *wdt_base = ioremap(CPHYSADDR(JZ4740_WDT_BASE_ADDR), 0x0f);
+
+ jz4740_timer_enable_watchdog();
+
+ writeb(0, wdt_base + JZ_REG_WDT_COUNTER_ENABLE);
+
+ writew(0, wdt_base + JZ_REG_WDT_COUNTER);
+ writew(0, wdt_base + JZ_REG_WDT_DATA);
+ writew(BIT(2), wdt_base + JZ_REG_WDT_CTRL);
+
+ writeb(1, wdt_base + JZ_REG_WDT_COUNTER_ENABLE);
+ jz4740_halt();
+}
+
+#define JZ_REG_RTC_CTRL 0x00
+#define JZ_REG_RTC_HIBERNATE 0x20
+
+#define JZ_RTC_CTRL_WRDY BIT(7)
+
+static void jz4740_power_off(void)
+{
+ void __iomem *rtc_base = ioremap(CPHYSADDR(JZ4740_RTC_BASE_ADDR), 0x24);
+ uint32_t ctrl;
+
+ do {
+ ctrl = readl(rtc_base + JZ_REG_RTC_CTRL);
+ } while (!(ctrl & JZ_RTC_CTRL_WRDY));
+
+ writel(1, rtc_base + JZ_REG_RTC_HIBERNATE);
+ jz4740_halt();
+}
+
+void jz4740_reset_init(void)
+{
+ _machine_restart = jz4740_restart;
+ _machine_halt = jz4740_halt;
+ pm_power_off = jz4740_power_off;
+}
diff --git a/arch/mips/jz4740/reset.h b/arch/mips/jz4740/reset.h
new file mode 100644
index 0000000..c57a829
--- /dev/null
+++ b/arch/mips/jz4740/reset.h
@@ -0,0 +1,7 @@
+#ifndef __MIPS_JZ4740_RESET_H__
+#define __MIPS_JZ4740_RESET_H__
+
+extern void jz4740_reset_init(void);
+
+#endif
+
--
1.5.6.5
This patch add clocksource and clockevent support for the timer/counter unit on
JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Thomas Gleixner <[email protected]>
---
arch/mips/jz4740/time.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 144 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/time.c
diff --git a/arch/mips/jz4740/time.c b/arch/mips/jz4740/time.c
new file mode 100644
index 0000000..da8183f
--- /dev/null
+++ b/arch/mips/jz4740/time.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform time support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+
+#include <linux/clockchips.h>
+
+#include <asm/mach-jz4740/irq.h>
+#include <asm/time.h>
+
+#include "clock.h"
+#include "timer.h"
+
+#define TIMER_CLOCKEVENT 0
+#define TIMER_CLOCKSOURCE 1
+
+static uint16_t jz4740_jiffies_per_tick;
+
+static cycle_t jz4740_clocksource_read(struct clocksource *cs)
+{
+ return jz4740_timer_get_count(TIMER_CLOCKSOURCE);
+}
+
+static struct clocksource jz4740_clocksource = {
+ .name = "jz4740-timer",
+ .rating = 200,
+ .read = jz4740_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(16),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static irqreturn_t jz4740_clockevent_irq(int irq, void *devid)
+{
+ struct clock_event_device *cd = devid;
+
+ jz4740_timer_ack_full(TIMER_CLOCKEVENT);
+
+ if (cd->mode != CLOCK_EVT_MODE_PERIODIC)
+ jz4740_timer_disable(TIMER_CLOCKEVENT);
+
+ cd->event_handler(cd);
+
+ return IRQ_HANDLED;
+}
+
+static void jz4740_clockevent_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *cd)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ jz4740_timer_set_count(TIMER_CLOCKEVENT, 0);
+ jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick);
+ case CLOCK_EVT_MODE_RESUME:
+ jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT);
+ jz4740_timer_enable(TIMER_CLOCKEVENT);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ jz4740_timer_disable(TIMER_CLOCKEVENT);
+ break;
+ default:
+ break;
+ }
+}
+
+static int jz4740_clockevent_set_next(unsigned long evt,
+ struct clock_event_device *cd)
+{
+ jz4740_timer_set_count(TIMER_CLOCKEVENT, 0);
+ jz4740_timer_set_period(TIMER_CLOCKEVENT, evt);
+ jz4740_timer_enable(TIMER_CLOCKEVENT);
+
+ return 0;
+}
+
+static struct clock_event_device jz4740_clockevent = {
+ .name = "jz4740-timer",
+ .features = CLOCK_EVT_FEAT_PERIODIC,
+ .set_next_event = jz4740_clockevent_set_next,
+ .set_mode = jz4740_clockevent_set_mode,
+ .rating = 200,
+ .irq = JZ4740_IRQ_TCU0,
+};
+
+static struct irqaction timer_irqaction = {
+ .handler = jz4740_clockevent_irq,
+ .flags = IRQF_PERCPU | IRQF_TIMER | IRQF_DISABLED,
+ .name = "jz4740-timerirq",
+ .dev_id = &jz4740_clockevent,
+};
+
+void __init plat_time_init(void)
+{
+ int ret;
+ uint32_t clk_rate;
+ uint16_t ctrl;
+
+ jz4740_timer_init();
+
+ clk_rate = jz4740_clock_bdata.ext_rate >> 4;
+ jz4740_jiffies_per_tick = DIV_ROUND_CLOSEST(clk_rate, HZ);
+
+ clockevent_set_clock(&jz4740_clockevent, clk_rate);
+ jz4740_clockevent.min_delta_ns = clockevent_delta2ns(100, &jz4740_clockevent);
+ jz4740_clockevent.max_delta_ns = clockevent_delta2ns(0xffff, &jz4740_clockevent);
+ jz4740_clockevent.cpumask = cpumask_of(0);
+
+ clockevents_register_device(&jz4740_clockevent);
+
+ clocksource_set_clock(&jz4740_clocksource, clk_rate);
+ ret = clocksource_register(&jz4740_clocksource);
+
+ if (ret)
+ printk(KERN_ERR "Failed to register clocksource: %d\n", ret);
+
+ setup_irq(JZ4740_IRQ_TCU0, &timer_irqaction);
+
+ ctrl = JZ_TIMER_CTRL_PRESCALE_16 | JZ_TIMER_CTRL_SRC_EXT;
+
+ jz4740_timer_set_ctrl(TIMER_CLOCKEVENT, ctrl);
+ jz4740_timer_set_ctrl(TIMER_CLOCKSOURCE, ctrl);
+
+ jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick);
+ jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT);
+
+ jz4740_timer_set_period(TIMER_CLOCKSOURCE, 0xffff);
+
+ jz4740_timer_enable(TIMER_CLOCKEVENT);
+ jz4740_timer_enable(TIMER_CLOCKSOURCE);
+}
--
1.5.6.5
This patch adds support for IRQ handling on a JZ4740 SoC.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Thomas Gleixner <[email protected]>
---
arch/mips/include/asm/mach-jz4740/irq.h | 55 ++++++++++
arch/mips/jz4740/irq.c | 170 +++++++++++++++++++++++++++++++
arch/mips/jz4740/irq.h | 21 ++++
3 files changed, 246 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/irq.h
create mode 100644 arch/mips/jz4740/irq.c
create mode 100644 arch/mips/jz4740/irq.h
diff --git a/arch/mips/include/asm/mach-jz4740/irq.h b/arch/mips/include/asm/mach-jz4740/irq.h
new file mode 100644
index 0000000..5e27b78
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/irq.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ7420/JZ4740 IRQ definitions
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __ASM_MACH_JZ4740_IRQ_H__
+#define __ASM_MACH_JZ4740_IRQ_H__
+
+#define MIPS_CPU_IRQ_BASE 0
+#define JZ4740_IRQ_BASE 8
+
+/* 1st-level interrupts */
+#define JZ4740_IRQ(x) (JZ4740_IRQ_BASE + (x))
+#define JZ4740_IRQ_I2C JZ4740_IRQ(1)
+#define JZ4740_IRQ_UHC JZ4740_IRQ(3)
+#define JZ4740_IRQ_UART1 JZ4740_IRQ(8)
+#define JZ4740_IRQ_UART0 JZ4740_IRQ(9)
+#define JZ4740_IRQ_SADC JZ4740_IRQ(12)
+#define JZ4740_IRQ_MSC JZ4740_IRQ(14)
+#define JZ4740_IRQ_RTC JZ4740_IRQ(15)
+#define JZ4740_IRQ_SSI JZ4740_IRQ(16)
+#define JZ4740_IRQ_CIM JZ4740_IRQ(17)
+#define JZ4740_IRQ_AIC JZ4740_IRQ(18)
+#define JZ4740_IRQ_ETH JZ4740_IRQ(19)
+#define JZ4740_IRQ_DMAC JZ4740_IRQ(20)
+#define JZ4740_IRQ_TCU2 JZ4740_IRQ(21)
+#define JZ4740_IRQ_TCU1 JZ4740_IRQ(22)
+#define JZ4740_IRQ_TCU0 JZ4740_IRQ(23)
+#define JZ4740_IRQ_UDC JZ4740_IRQ(24)
+#define JZ4740_IRQ_GPIO3 JZ4740_IRQ(25)
+#define JZ4740_IRQ_GPIO2 JZ4740_IRQ(26)
+#define JZ4740_IRQ_GPIO1 JZ4740_IRQ(27)
+#define JZ4740_IRQ_GPIO0 JZ4740_IRQ(28)
+#define JZ4740_IRQ_IPU JZ4740_IRQ(29)
+#define JZ4740_IRQ_LCD JZ4740_IRQ(30)
+
+/* 2nd-level interrupts */
+#define JZ4740_IRQ_DMA(x) ((x) + JZ4740_IRQ(32))
+
+#define JZ4740_IRQ_INTC_GPIO(x) (JZ4740_IRQ_GPIO0 - (x))
+#define JZ4740_IRQ_GPIO(x) (JZ4740_IRQ(48) + (x))
+
+#define NR_IRQS (JZ4740_IRQ_GPIO(127) + 1)
+
+#endif
diff --git a/arch/mips/jz4740/irq.c b/arch/mips/jz4740/irq.c
new file mode 100644
index 0000000..46a03ee
--- /dev/null
+++ b/arch/mips/jz4740/irq.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform IRQ support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <asm/irq_cpu.h>
+
+#include <asm/mach-jz4740/base.h>
+
+static void __iomem *jz_intc_base;
+static uint32_t jz_intc_wakeup;
+static uint32_t jz_intc_saved;
+
+#define JZ_REG_INTC_STATUS 0x00
+#define JZ_REG_INTC_MASK 0x04
+#define JZ_REG_INTC_SET_MASK 0x08
+#define JZ_REG_INTC_CLEAR_MASK 0x0c
+#define JZ_REG_INTC_PENDING 0x10
+
+#define IRQ_BIT(x) BIT((x) - JZ4740_IRQ_BASE)
+
+static void intc_irq_unmask(unsigned int irq)
+{
+ writel(IRQ_BIT(irq), jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
+}
+
+static void intc_irq_mask(unsigned int irq)
+{
+ writel(IRQ_BIT(irq), jz_intc_base + JZ_REG_INTC_SET_MASK);
+}
+
+static int intc_irq_set_wake(unsigned int irq, unsigned int on)
+{
+ if (on)
+ jz_intc_wakeup |= IRQ_BIT(irq);
+ else
+ jz_intc_wakeup &= ~IRQ_BIT(irq);
+
+ return 0;
+}
+
+static struct irq_chip intc_irq_type = {
+ .name = "INTC",
+ .mask = intc_irq_mask,
+ .mask_ack = intc_irq_mask,
+ .unmask = intc_irq_unmask,
+ .set_wake = intc_irq_set_wake,
+};
+
+static irqreturn_t jz4740_cascade(int irq, void *data)
+{
+ uint32_t irq_reg;
+ int intc_irq;
+
+ irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING);
+
+ intc_irq = ffs(irq_reg);
+ if (intc_irq)
+ generic_handle_irq(intc_irq - 1 + JZ4740_IRQ_BASE);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction jz4740_cascade_action = {
+ .handler = jz4740_cascade,
+ .name = "JZ4740 cascade interrupt",
+ .flags = IRQF_DISABLED,
+};
+
+void __init arch_init_irq(void)
+{
+ int i;
+ mips_cpu_irq_init();
+
+ jz_intc_base = ioremap(CPHYSADDR(JZ4740_INTC_BASE_ADDR), 0x14);
+
+ for (i = JZ4740_IRQ_BASE; i < JZ4740_IRQ_BASE + 32; i++) {
+ intc_irq_mask(i);
+ set_irq_chip_and_handler(i, &intc_irq_type, handle_level_irq);
+ }
+
+ setup_irq(2, &jz4740_cascade_action);
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
+ if (pending & STATUSF_IP2)
+ do_IRQ(2);
+ else if (pending & STATUSF_IP3)
+ do_IRQ(3);
+ else
+ spurious_interrupt();
+}
+
+void jz4740_intc_suspend(void)
+{
+ jz_intc_saved = readl(jz_intc_base + JZ_REG_INTC_MASK);
+ writel(~jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_SET_MASK);
+ writel(jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
+}
+
+void jz4740_intc_resume(void)
+{
+ writel(~jz_intc_saved, jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
+ writel(jz_intc_saved, jz_intc_base + JZ_REG_INTC_SET_MASK);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static inline void intc_seq_reg(struct seq_file *s, const char *name,
+ unsigned int reg)
+{
+ seq_printf(s, "%s:\t\t%08x\n", name, readl(jz_intc_base + reg));
+}
+
+static int intc_regs_show(struct seq_file *s, void *unused)
+{
+ intc_seq_reg(s, "Status", JZ_REG_INTC_STATUS);
+ intc_seq_reg(s, "Mask", JZ_REG_INTC_MASK);
+ intc_seq_reg(s, "Pending", JZ_REG_INTC_PENDING);
+
+ return 0;
+}
+
+static int intc_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, intc_regs_show, NULL);
+}
+
+static const struct file_operations intc_regs_operations = {
+ .open = intc_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init intc_debugfs_init(void)
+{
+ (void) debugfs_create_file("jz_regs_intc", S_IFREG | S_IRUGO,
+ NULL, NULL, &intc_regs_operations);
+ return 0;
+}
+subsys_initcall(intc_debugfs_init);
+
+#endif
diff --git a/arch/mips/jz4740/irq.h b/arch/mips/jz4740/irq.h
new file mode 100644
index 0000000..dadbd5f
--- /dev/null
+++ b/arch/mips/jz4740/irq.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __MIPS_JZ4740_IRQ_H__
+#define __MIPS_JZ4740_IRQ_H__
+
+extern void jz4740_intc_suspend(void);
+extern void jz4740_intc_resume(void);
+
+#endif
--
1.5.6.5
This patch adds a new cpu type for the JZ4740 to the Linux MIPS architecture code.
It also adds the iomem addresses for the different components found on a JZ4740
SoC.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/include/asm/bootinfo.h | 6 ++++++
arch/mips/include/asm/cpu.h | 9 ++++++++-
arch/mips/include/asm/mach-jz4740/base.h | 28 ++++++++++++++++++++++++++++
arch/mips/include/asm/mach-jz4740/war.h | 25 +++++++++++++++++++++++++
arch/mips/kernel/cpu-probe.c | 20 ++++++++++++++++++++
arch/mips/mm/tlbex.c | 5 +++++
6 files changed, 92 insertions(+), 1 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/base.h
create mode 100644 arch/mips/include/asm/mach-jz4740/war.h
diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h
index 09eee09..15a8ef0 100644
--- a/arch/mips/include/asm/bootinfo.h
+++ b/arch/mips/include/asm/bootinfo.h
@@ -71,6 +71,12 @@
#define MACH_LEMOTE_LL2F 7
#define MACH_LOONGSON_END 8
+/*
+ * Valid machtype for group INGENIC
+ */
+#define MACH_INGENIC_JZ4730 0 /* JZ4730 SOC */
+#define MACH_INGENIC_JZ4740 1 /* JZ4740 SOC */
+
extern char *system_type;
const char *get_system_type(void);
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index a5acda4..b201a8f 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -34,7 +34,7 @@
#define PRID_COMP_LSI 0x080000
#define PRID_COMP_LEXRA 0x0b0000
#define PRID_COMP_CAVIUM 0x0d0000
-
+#define PRID_COMP_INGENIC 0xd00000
/*
* Assigned values for the product ID register. In order to detect a
@@ -133,6 +133,12 @@
#define PRID_IMP_CAVIUM_CN52XX 0x0700
/*
+ * These are the PRID's for when 23:16 == PRID_COMP_INGENIC
+ */
+
+#define PRID_IMP_JZRISC 0x0200
+
+/*
* Definitions for 7:0 on legacy processors
*/
@@ -219,6 +225,7 @@ enum cpu_type_enum {
CPU_4KC, CPU_4KEC, CPU_4KSC, CPU_24K, CPU_34K, CPU_1004K, CPU_74K,
CPU_ALCHEMY, CPU_PR4450, CPU_BCM3302, CPU_BCM4710,
CPU_BCM6338, CPU_BCM6345, CPU_BCM6348, CPU_BCM6358,
+ CPU_JZRISC,
/*
* MIPS64 class processors
diff --git a/arch/mips/include/asm/mach-jz4740/base.h b/arch/mips/include/asm/mach-jz4740/base.h
new file mode 100644
index 0000000..cba3aae
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/base.h
@@ -0,0 +1,28 @@
+#ifndef __ASM_MACH_JZ4740_BASE_H__
+#define __ASM_MACH_JZ4740_BASE_H__
+
+#define JZ4740_CPM_BASE_ADDR 0xb0000000
+#define JZ4740_INTC_BASE_ADDR 0xb0001000
+#define JZ4740_TCU_BASE_ADDR 0xb0002000
+#define JZ4740_WDT_BASE_ADDR 0xb0002000
+#define JZ4740_RTC_BASE_ADDR 0xb0003000
+#define JZ4740_GPIO_BASE_ADDR 0xb0010000
+#define JZ4740_AIC_BASE_ADDR 0xb0020000
+#define JZ4740_ICDC_BASE_ADDR 0xb0020000
+#define JZ4740_MSC_BASE_ADDR 0xb0021000
+#define JZ4740_UART0_BASE_ADDR 0xb0030000
+#define JZ4740_UART1_BASE_ADDR 0xb0031000
+#define JZ4740_I2C_BASE_ADDR 0xb0042000
+#define JZ4740_SSI_BASE_ADDR 0xb0043000
+#define JZ4740_SADC_BASE_ADDR 0xb0070000
+#define JZ4740_EMC_BASE_ADDR 0xb3010000
+#define JZ4740_DMAC_BASE_ADDR 0xb3020000
+#define JZ4740_UHC_BASE_ADDR 0xb3030000
+#define JZ4740_UDC_BASE_ADDR 0xb3040000
+#define JZ4740_LCD_BASE_ADDR 0xb3050000
+#define JZ4740_SLCD_BASE_ADDR 0xb3050000
+#define JZ4740_CIM_BASE_ADDR 0xb3060000
+#define JZ4740_IPU_BASE_ADDR 0xb3080000
+#define JZ4740_ETH_BASE_ADDR 0xb3100000
+
+#endif
diff --git a/arch/mips/include/asm/mach-jz4740/war.h b/arch/mips/include/asm/mach-jz4740/war.h
new file mode 100644
index 0000000..3a5bc17
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/war.h
@@ -0,0 +1,25 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <[email protected]>
+ */
+#ifndef __ASM_MIPS_MACH_JZ4740_WAR_H
+#define __ASM_MIPS_MACH_JZ4740_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR 0
+#define R4600_V1_HIT_CACHEOP_WAR 0
+#define R4600_V2_HIT_CACHEOP_WAR 0
+#define R5432_CP0_INTERRUPT_WAR 0
+#define BCM1250_M3_WAR 0
+#define SIBYTE_1956_WAR 0
+#define MIPS4K_ICACHE_REFILL_WAR 0
+#define MIPS_CACHE_SYNC_WAR 0
+#define TX49XX_ICACHE_INDEX_INV_WAR 0
+#define RM9000_CDEX_SMP_WAR 0
+#define ICACHE_REFILLS_WORKAROUND_WAR 0
+#define R10000_LLSC_WAR 0
+#define MIPS34K_MISSED_ITLB_WAR 0
+
+#endif /* __ASM_MIPS_MACH_JZ4740_WAR_H */
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index 3562b85..9b66331 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -187,6 +187,7 @@ void __init check_wait(void)
case CPU_BCM6358:
case CPU_CAVIUM_OCTEON:
case CPU_CAVIUM_OCTEON_PLUS:
+ case CPU_JZRISC:
cpu_wait = r4k_wait;
break;
@@ -956,6 +957,22 @@ platform:
}
}
+static inline void cpu_probe_ingenic(struct cpuinfo_mips *c, unsigned int cpu)
+{
+ decode_configs(c);
+ /* JZRISC does not implement the CP0 counter. */
+ c->options &= ~MIPS_CPU_COUNTER;
+ switch (c->processor_id & 0xff00) {
+ case PRID_IMP_JZRISC:
+ c->cputype = CPU_JZRISC;
+ __cpu_name[cpu] = "Ingenic JZRISC";
+ break;
+ default:
+ panic("Unknown Ingenic Processor ID!");
+ break;
+ }
+}
+
const char *__cpu_name[NR_CPUS];
const char *__elf_platform;
@@ -994,6 +1011,9 @@ __cpuinit void cpu_probe(void)
case PRID_COMP_CAVIUM:
cpu_probe_cavium(c, cpu);
break;
+ case PRID_COMP_INGENIC:
+ cpu_probe_ingenic(c, cpu);
+ break;
}
BUG_ON(!__cpu_name[cpu]);
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 86f004d..4510e61 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -409,6 +409,11 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l,
tlbw(p);
break;
+ case CPU_JZRISC:
+ tlbw(p);
+ uasm_i_nop(p);
+ break;
+
default:
panic("No TLB refill handler yet (CPU type: %d)",
current_cpu_data.cputype);
--
1.5.6.5
This patch adds support for managing the clocks found on JZ4740 SoC through
the Linux clock API.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/include/asm/mach-jz4740/clock.h | 28 +
arch/mips/jz4740/clock-debugfs.c | 109 ++++
arch/mips/jz4740/clock.c | 920 +++++++++++++++++++++++++++++
arch/mips/jz4740/clock.h | 76 +++
4 files changed, 1133 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/clock.h
create mode 100644 arch/mips/jz4740/clock-debugfs.c
create mode 100644 arch/mips/jz4740/clock.c
create mode 100644 arch/mips/jz4740/clock.h
diff --git a/arch/mips/include/asm/mach-jz4740/clock.h b/arch/mips/include/asm/mach-jz4740/clock.h
new file mode 100644
index 0000000..9069727
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/clock.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __ASM_JZ4740_CLOCK_H__
+#define __ASM_JZ4740_CLOCK_H__
+
+enum jz4740_wait_mode {
+ JZ4740_WAIT_MODE_IDLE,
+ JZ4740_WAIT_MODE_SLEEP,
+};
+
+void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode);
+
+void jz4740_clock_udc_enable_auto_suspend(void);
+void jz4740_clock_udc_disable_auto_suspend(void);
+
+#endif
diff --git a/arch/mips/jz4740/clock-debugfs.c b/arch/mips/jz4740/clock-debugfs.c
new file mode 100644
index 0000000..993b91b
--- /dev/null
+++ b/arch/mips/jz4740/clock-debugfs.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC clock support debugfs entries
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#include <asm/mach-jz4740/clock.h>
+#include "clock.h"
+
+static struct dentry *jz4740_clock_debugfs;
+
+static int jz4740_clock_debugfs_show_enabled(void *data, uint64_t *value)
+{
+ struct clk *clk = data;
+ *value = clk_is_enabled(clk);
+
+ return 0;
+}
+
+static int jz4740_clock_debugfs_set_enabled(void *data, uint64_t value)
+{
+ struct clk *clk = data;
+
+ if (value)
+ return clk_enable(clk);
+ else
+ clk_disable(clk);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(jz4740_clock_debugfs_ops_enabled,
+ jz4740_clock_debugfs_show_enabled,
+ jz4740_clock_debugfs_set_enabled,
+ "%llu\n");
+
+static int jz4740_clock_debugfs_show_rate(void *data, uint64_t *value)
+{
+ struct clk *clk = data;
+ *value = clk_get_rate(clk);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(jz4740_clock_debugfs_ops_rate,
+ jz4740_clock_debugfs_show_rate,
+ NULL,
+ "%llu\n");
+
+void jz4740_clock_debugfs_add_clk(struct clk *clk)
+{
+ if (!jz4740_clock_debugfs)
+ return;
+
+ clk->debugfs_entry = debugfs_create_dir(clk->name, jz4740_clock_debugfs);
+ debugfs_create_file("rate", S_IWUGO | S_IRUGO, clk->debugfs_entry, clk,
+ &jz4740_clock_debugfs_ops_rate);
+ debugfs_create_file("enabled", S_IRUGO, clk->debugfs_entry, clk,
+ &jz4740_clock_debugfs_ops_enabled);
+
+ if (clk->parent) {
+ char parent_path[100];
+ snprintf(parent_path, 100, "../%s", clk->parent->name);
+ clk->debugfs_parent_entry = debugfs_create_symlink("parent",
+ clk->debugfs_entry,
+ parent_path);
+ }
+}
+
+/* TODO: Locking */
+void jz4740_clock_debugfs_update_parent(struct clk *clk)
+{
+ if (clk->debugfs_parent_entry)
+ debugfs_remove(clk->debugfs_parent_entry);
+
+ if (clk->parent) {
+ char parent_path[100];
+ snprintf(parent_path, 100, "../%s", clk->parent->name);
+ clk->debugfs_parent_entry = debugfs_create_symlink("parent",
+ clk->debugfs_entry,
+ parent_path);
+ } else {
+ clk->debugfs_parent_entry = NULL;
+ }
+}
+
+void jz4740_clock_debugfs_init(void)
+{
+ jz4740_clock_debugfs = debugfs_create_dir("jz4740-clock", NULL);
+ if (IS_ERR(jz4740_clock_debugfs))
+ jz4740_clock_debugfs = NULL;
+}
diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c
new file mode 100644
index 0000000..378f2b0
--- /dev/null
+++ b/arch/mips/jz4740/clock.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC clock support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/err.h>
+
+#include <asm/mach-jz4740/clock.h>
+#include <asm/mach-jz4740/base.h>
+
+#include "clock.h"
+
+#define JZ_REG_CLOCK_CTRL 0x00
+#define JZ_REG_CLOCK_LOW_POWER 0x04
+#define JZ_REG_CLOCK_PLL 0x10
+#define JZ_REG_CLOCK_GATE 0x20
+#define JZ_REG_CLOCK_SLEEP_CTRL 0x24
+#define JZ_REG_CLOCK_I2S 0x60
+#define JZ_REG_CLOCK_LCD 0x64
+#define JZ_REG_CLOCK_MMC 0x68
+#define JZ_REG_CLOCK_UHC 0x6C
+#define JZ_REG_CLOCK_SPI 0x74
+
+#define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31)
+#define JZ_CLOCK_CTRL_KO_ENABLE BIT(30)
+#define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29)
+#define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000
+#define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22)
+#define JZ_CLOCK_CTRL_PLL_HALF BIT(21)
+#define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000
+#define JZ_CLOCK_CTRL_UDIV_OFFSET 23
+#define JZ_CLOCK_CTRL_LDIV_OFFSET 16
+#define JZ_CLOCK_CTRL_MDIV_OFFSET 12
+#define JZ_CLOCK_CTRL_PDIV_OFFSET 8
+#define JZ_CLOCK_CTRL_HDIV_OFFSET 4
+#define JZ_CLOCK_CTRL_CDIV_OFFSET 0
+
+#define JZ_CLOCK_GATE_UART0 BIT(0)
+#define JZ_CLOCK_GATE_TCU BIT(1)
+#define JZ_CLOCK_GATE_RTC BIT(2)
+#define JZ_CLOCK_GATE_I2C BIT(3)
+#define JZ_CLOCK_GATE_SPI BIT(4)
+#define JZ_CLOCK_GATE_AIC BIT(5)
+#define JZ_CLOCK_GATE_I2S BIT(6)
+#define JZ_CLOCK_GATE_MMC BIT(7)
+#define JZ_CLOCK_GATE_ADC BIT(8)
+#define JZ_CLOCK_GATE_CIM BIT(9)
+#define JZ_CLOCK_GATE_LCD BIT(10)
+#define JZ_CLOCK_GATE_UDC BIT(11)
+#define JZ_CLOCK_GATE_DMAC BIT(12)
+#define JZ_CLOCK_GATE_IPU BIT(13)
+#define JZ_CLOCK_GATE_UHC BIT(14)
+#define JZ_CLOCK_GATE_UART1 BIT(15)
+
+#define JZ_CLOCK_I2S_DIV_MASK 0x01ff
+
+#define JZ_CLOCK_LCD_DIV_MASK 0x01ff
+
+#define JZ_CLOCK_MMC_DIV_MASK 0x001f
+
+#define JZ_CLOCK_UHC_DIV_MASK 0x000f
+
+#define JZ_CLOCK_SPI_SRC_PLL BIT(31)
+#define JZ_CLOCK_SPI_DIV_MASK 0x000f
+
+#define JZ_CLOCK_PLL_M_MASK 0x01ff
+#define JZ_CLOCK_PLL_N_MASK 0x001f
+#define JZ_CLOCK_PLL_OD_MASK 0x0003
+#define JZ_CLOCK_PLL_STABLE BIT(10)
+#define JZ_CLOCK_PLL_BYPASS BIT(9)
+#define JZ_CLOCK_PLL_ENABLED BIT(8)
+#define JZ_CLOCK_PLL_STABLIZE_MASK 0x000f
+#define JZ_CLOCK_PLL_M_OFFSET 23
+#define JZ_CLOCK_PLL_N_OFFSET 18
+#define JZ_CLOCK_PLL_OD_OFFSET 16
+
+#define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2)
+#define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0)
+
+#define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7)
+#define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6)
+
+static void __iomem *jz_clock_base;
+static spinlock_t jz_clock_lock;
+static LIST_HEAD(jz_clocks);
+
+struct main_clk {
+ struct clk clk;
+ uint32_t div_offset;
+};
+
+struct divided_clk {
+ struct clk clk;
+ uint32_t reg;
+ uint32_t mask;
+};
+
+struct static_clk {
+ struct clk clk;
+ unsigned long rate;
+};
+
+static uint32_t jz_clk_reg_read(int reg)
+{
+ return readl(jz_clock_base + reg);
+}
+
+static void jz_clk_reg_write_mask(int reg, uint32_t val, uint32_t mask)
+{
+ uint32_t val2;
+
+ spin_lock(&jz_clock_lock);
+ val2 = readl(jz_clock_base + reg);
+ val2 &= ~mask;
+ val2 |= val;
+ writel(val2, jz_clock_base + reg);
+ spin_unlock(&jz_clock_lock);
+}
+
+static void jz_clk_reg_set_bits(int reg, uint32_t mask)
+{
+ uint32_t val;
+
+ spin_lock(&jz_clock_lock);
+ val = readl(jz_clock_base + reg);
+ val |= mask;
+ writel(val, jz_clock_base + reg);
+ spin_unlock(&jz_clock_lock);
+}
+
+static void jz_clk_reg_clear_bits(int reg, uint32_t mask)
+{
+ uint32_t val;
+
+ spin_lock(&jz_clock_lock);
+ val = readl(jz_clock_base + reg);
+ val &= ~mask;
+ writel(val, jz_clock_base + reg);
+ spin_unlock(&jz_clock_lock);
+}
+
+static int jz_clk_enable_gating(struct clk *clk)
+{
+ if (clk->gate_bit == JZ4740_CLK_NOT_GATED)
+ return -EINVAL;
+
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_GATE, clk->gate_bit);
+ return 0;
+}
+
+static int jz_clk_disable_gating(struct clk *clk)
+{
+ if (clk->gate_bit == JZ4740_CLK_NOT_GATED)
+ return -EINVAL;
+
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_GATE, clk->gate_bit);
+ return 0;
+}
+
+static int jz_clk_is_enabled_gating(struct clk *clk)
+{
+ if (clk->gate_bit == JZ4740_CLK_NOT_GATED)
+ return 1;
+
+ return !(jz_clk_reg_read(JZ_REG_CLOCK_GATE) & clk->gate_bit);
+}
+
+static unsigned long jz_clk_static_get_rate(struct clk *clk)
+{
+ return ((struct static_clk *)clk)->rate;
+}
+
+static int jz_clk_ko_enable(struct clk *clk)
+{
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_KO_ENABLE);
+ return 0;
+}
+
+static int jz_clk_ko_disable(struct clk *clk)
+{
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_KO_ENABLE);
+ return 0;
+}
+
+static int jz_clk_ko_is_enabled(struct clk *clk)
+{
+ return !!(jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_KO_ENABLE);
+}
+
+static const int pllno[] = {1, 2, 2, 4};
+
+static unsigned long jz_clk_pll_get_rate(struct clk *clk)
+{
+ uint32_t val;
+ int m;
+ int n;
+ int od;
+
+ val = jz_clk_reg_read(JZ_REG_CLOCK_PLL);
+
+ if (val & JZ_CLOCK_PLL_BYPASS)
+ return clk_get_rate(clk->parent);
+
+ m = ((val >> 23) & 0x1ff) + 2;
+ n = ((val >> 18) & 0x1f) + 2;
+ od = (val >> 16) & 0x3;
+
+ return clk_get_rate(clk->parent) * (m / n) / pllno[od];
+}
+
+static unsigned long jz_clk_pll_half_get_rate(struct clk *clk)
+{
+ uint32_t reg;
+
+ reg = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
+ if (reg & JZ_CLOCK_CTRL_PLL_HALF)
+ return jz_clk_pll_get_rate(clk->parent);
+ return jz_clk_pll_get_rate(clk->parent) >> 1;
+}
+
+static const int jz_clk_main_divs[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
+
+static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long parent_rate = jz_clk_pll_get_rate(clk->parent);
+ int div;
+
+ div = parent_rate / rate;
+ if (div > 32)
+ return parent_rate / 32;
+ else if (div < 1)
+ return parent_rate;
+
+ div &= (0x3 << (ffs(div) - 1));
+
+ return parent_rate / div;
+}
+
+static unsigned long jz_clk_main_get_rate(struct clk *clk)
+{
+ struct main_clk *mclk = (struct main_clk *)clk;
+ uint32_t div;
+
+ div = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
+
+ div >>= mclk->div_offset;
+ div &= 0xf;
+
+ if (div >= ARRAY_SIZE(jz_clk_main_divs))
+ div = ARRAY_SIZE(jz_clk_main_divs) - 1;
+
+ return jz_clk_pll_get_rate(clk->parent) / jz_clk_main_divs[div];
+}
+
+static int jz_clk_main_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct main_clk *mclk = (struct main_clk *)clk;
+ int i;
+ int div;
+ unsigned long parent_rate = jz_clk_pll_get_rate(clk->parent);
+
+ rate = jz_clk_main_round_rate(clk, rate);
+
+ div = parent_rate / rate;
+
+ i = (ffs(div) - 1) << 1;
+ if (i > 0 && !(div & BIT(i-1)))
+ i -= 1;
+
+ jz_clk_reg_write_mask(JZ_REG_CLOCK_CTRL, i << mclk->div_offset,
+ 0xf << mclk->div_offset);
+
+ return 0;
+}
+
+static struct clk_ops jz_clk_static_ops = {
+ .get_rate = jz_clk_static_get_rate,
+ .enable = jz_clk_enable_gating,
+ .disable = jz_clk_disable_gating,
+ .is_enabled = jz_clk_is_enabled_gating,
+};
+
+static struct static_clk jz_clk_ext = {
+ .clk = {
+ .name = "ext",
+ .gate_bit = JZ4740_CLK_NOT_GATED,
+ .ops = &jz_clk_static_ops,
+ },
+};
+
+static struct clk_ops jz_clk_pll_ops = {
+ .get_rate = jz_clk_static_get_rate,
+};
+
+static struct clk jz_clk_pll = {
+ .name = "pll",
+ .parent = &jz_clk_ext.clk,
+ .ops = &jz_clk_pll_ops,
+};
+
+static struct clk_ops jz_clk_pll_half_ops = {
+ .get_rate = jz_clk_pll_half_get_rate,
+};
+
+static struct clk jz_clk_pll_half = {
+ .name = "pll half",
+ .parent = &jz_clk_pll,
+ .ops = &jz_clk_pll_half_ops,
+};
+
+static const struct clk_ops jz_clk_main_ops = {
+ .get_rate = jz_clk_main_get_rate,
+ .set_rate = jz_clk_main_set_rate,
+ .round_rate = jz_clk_main_round_rate,
+};
+
+static struct main_clk jz_clk_cpu = {
+ .clk = {
+ .name = "cclk",
+ .parent = &jz_clk_pll,
+ .ops = &jz_clk_main_ops,
+ },
+ .div_offset = JZ_CLOCK_CTRL_CDIV_OFFSET,
+};
+
+static struct main_clk jz_clk_memory = {
+ .clk = {
+ .name = "mclk",
+ .parent = &jz_clk_pll,
+ .ops = &jz_clk_main_ops,
+ },
+ .div_offset = JZ_CLOCK_CTRL_MDIV_OFFSET,
+};
+
+static struct main_clk jz_clk_high_speed_peripheral = {
+ .clk = {
+ .name = "hclk",
+ .parent = &jz_clk_pll,
+ .ops = &jz_clk_main_ops,
+ },
+ .div_offset = JZ_CLOCK_CTRL_HDIV_OFFSET,
+};
+
+
+static struct main_clk jz_clk_low_speed_peripheral = {
+ .clk = {
+ .name = "pclk",
+ .parent = &jz_clk_pll,
+ .ops = &jz_clk_main_ops,
+ },
+ .div_offset = JZ_CLOCK_CTRL_PDIV_OFFSET,
+};
+
+static const struct clk_ops jz_clk_ko_ops = {
+ .enable = jz_clk_ko_enable,
+ .disable = jz_clk_ko_disable,
+ .is_enabled = jz_clk_ko_is_enabled,
+};
+
+static struct clk jz_clk_ko = {
+ .name = "cko",
+ .parent = &jz_clk_memory.clk,
+ .ops = &jz_clk_ko_ops,
+};
+
+static int jz_clk_spi_set_parent(struct clk *clk, struct clk *parent)
+{
+ if (parent == &jz_clk_pll)
+ jz_clk_reg_set_bits(JZ_CLOCK_SPI_SRC_PLL, JZ_REG_CLOCK_SPI);
+ else if (parent == &jz_clk_ext.clk)
+ jz_clk_reg_clear_bits(JZ_CLOCK_SPI_SRC_PLL, JZ_REG_CLOCK_SPI);
+ else
+ return -EINVAL;
+
+ clk->parent = parent;
+
+ return 0;
+}
+
+static int jz_clk_i2s_set_parent(struct clk *clk, struct clk *parent)
+{
+ if (parent == &jz_clk_pll_half)
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_I2S_SRC_PLL);
+ else if (parent == &jz_clk_ext.clk)
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_I2S_SRC_PLL);
+ else
+ return -EINVAL;
+
+ clk->parent = parent;
+
+ return 0;
+}
+
+static int jz_clk_udc_enable(struct clk *clk)
+{
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_SLEEP_CTRL,
+ JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC);
+
+ return 0;
+}
+
+static int jz_clk_udc_disable(struct clk *clk)
+{
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_SLEEP_CTRL,
+ JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC);
+
+ return 0;
+}
+
+static int jz_clk_udc_is_enabled(struct clk *clk)
+{
+ return !!(jz_clk_reg_read(JZ_REG_CLOCK_SLEEP_CTRL) &
+ JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC);
+}
+static int jz_clk_udc_set_parent(struct clk *clk, struct clk *parent)
+{
+ if (parent == &jz_clk_pll_half)
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_UDC_SRC_PLL);
+ else if (parent == &jz_clk_ext.clk)
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_UDC_SRC_PLL);
+ else
+ return -EINVAL;
+
+ clk->parent = parent;
+
+ return 0;
+}
+
+static int jz_clk_udc_set_rate(struct clk *clk, unsigned long rate)
+{
+ int div;
+
+ if (clk->parent == &jz_clk_ext.clk)
+ return -EINVAL;
+
+ div = clk_get_rate(clk->parent) / rate - 1;
+
+ if (div < 0)
+ div = 0;
+ else if (div > 63)
+ div = 63;
+
+ jz_clk_reg_write_mask(JZ_REG_CLOCK_CTRL, div << JZ_CLOCK_CTRL_UDIV_OFFSET,
+ JZ_CLOCK_CTRL_UDIV_MASK);
+ return 0;
+}
+
+static unsigned long jz_clk_udc_get_rate(struct clk *clk)
+{
+ int div;
+
+ if (clk->parent == &jz_clk_ext.clk)
+ return clk_get_rate(clk->parent);
+
+ div = (jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_UDIV_MASK);
+ div >>= JZ_CLOCK_CTRL_UDIV_OFFSET;
+ div += 1;
+
+ return clk_get_rate(clk->parent) / div;
+}
+
+static unsigned long jz_clk_divided_get_rate(struct clk *clk)
+{
+ struct divided_clk *dclk = (struct divided_clk *)clk;
+ int div;
+
+ if (clk->parent == &jz_clk_ext.clk)
+ return clk_get_rate(clk->parent);
+
+ div = (jz_clk_reg_read(dclk->reg) & dclk->mask) + 1;
+
+ return clk_get_rate(clk->parent) / div;
+}
+
+static int jz_clk_divided_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct divided_clk *dclk = (struct divided_clk *)clk;
+ int div;
+
+ if (clk->parent == &jz_clk_ext.clk)
+ return -EINVAL;
+
+ div = clk_get_rate(clk->parent) / rate - 1;
+
+ if (div < 0)
+ div = 0;
+ else if (div > dclk->mask)
+ div = dclk->mask;
+
+ jz_clk_reg_write_mask(dclk->reg, div, dclk->mask);
+
+ return 0;
+}
+
+static unsigned long jz_clk_ldclk_round_rate(struct clk *clk, unsigned long rate)
+{
+ int div;
+ unsigned long parent_rate = jz_clk_pll_half_get_rate(clk->parent);
+
+ if (rate > 150000000)
+ return 150000000;
+
+ div = parent_rate / rate;
+ if (div < 1)
+ div = 1;
+ else if (div > 32)
+ div = 32;
+
+ return parent_rate / div;
+}
+
+static int jz_clk_ldclk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int div;
+
+ if (rate > 150000000)
+ return -EINVAL;
+
+ div = jz_clk_pll_half_get_rate(clk->parent) / rate - 1;
+ if (div < 0)
+ div = 0;
+ else if (div > 31)
+ div = 31;
+
+ jz_clk_reg_write_mask(JZ_REG_CLOCK_CTRL, div << JZ_CLOCK_CTRL_LDIV_OFFSET,
+ JZ_CLOCK_CTRL_LDIV_MASK);
+
+ return 0;
+}
+
+static unsigned long jz_clk_ldclk_get_rate(struct clk *clk)
+{
+ int div;
+
+ div = jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_LDIV_MASK;
+ div >>= JZ_CLOCK_CTRL_LDIV_OFFSET;
+
+ return jz_clk_pll_half_get_rate(clk->parent) / (div + 1);
+}
+
+static const struct clk_ops jz_clk_ops_ld = {
+ .set_rate = jz_clk_ldclk_set_rate,
+ .get_rate = jz_clk_ldclk_get_rate,
+ .round_rate = jz_clk_ldclk_round_rate,
+ .enable = jz_clk_enable_gating,
+ .disable = jz_clk_disable_gating,
+ .is_enabled = jz_clk_is_enabled_gating,
+};
+
+static struct clk jz_clk_ld = {
+ .name = "lcd",
+ .gate_bit = JZ_CLOCK_GATE_LCD,
+ .parent = &jz_clk_pll_half,
+ .ops = &jz_clk_ops_ld,
+};
+
+static const struct clk_ops jz_clk_i2s_ops = {
+ .set_rate = jz_clk_divided_set_rate,
+ .get_rate = jz_clk_divided_get_rate,
+ .enable = jz_clk_enable_gating,
+ .disable = jz_clk_disable_gating,
+ .is_enabled = jz_clk_is_enabled_gating,
+ .set_parent = jz_clk_i2s_set_parent,
+};
+
+static const struct clk_ops jz_clk_spi_ops = {
+ .set_rate = jz_clk_divided_set_rate,
+ .get_rate = jz_clk_divided_get_rate,
+ .enable = jz_clk_enable_gating,
+ .disable = jz_clk_disable_gating,
+ .is_enabled = jz_clk_is_enabled_gating,
+ .set_parent = jz_clk_spi_set_parent,
+};
+
+static const struct clk_ops jz_clk_divided_ops = {
+ .set_rate = jz_clk_divided_set_rate,
+ .get_rate = jz_clk_divided_get_rate,
+ .enable = jz_clk_enable_gating,
+ .disable = jz_clk_disable_gating,
+ .is_enabled = jz_clk_is_enabled_gating,
+};
+
+static struct divided_clk jz4740_clock_divided_clks[] = {
+ {
+ .clk = {
+ .name = "lcd_pclk",
+ .parent = &jz_clk_pll_half,
+ .gate_bit = JZ4740_CLK_NOT_GATED,
+ .ops = &jz_clk_divided_ops,
+ },
+ .reg = JZ_REG_CLOCK_LCD,
+ .mask = JZ_CLOCK_LCD_DIV_MASK,
+ },
+ {
+ .clk = {
+ .name = "i2s",
+ .parent = &jz_clk_ext.clk,
+ .gate_bit = JZ_CLOCK_GATE_I2S,
+ .ops = &jz_clk_i2s_ops,
+ },
+ .reg = JZ_REG_CLOCK_I2S,
+ .mask = JZ_CLOCK_I2S_DIV_MASK,
+ },
+ {
+ .clk = {
+ .name = "spi",
+ .parent = &jz_clk_ext.clk,
+ .gate_bit = JZ_CLOCK_GATE_SPI,
+ .ops = &jz_clk_spi_ops,
+ },
+ .reg = JZ_REG_CLOCK_SPI,
+ .mask = JZ_CLOCK_SPI_DIV_MASK,
+ },
+ {
+ .clk = {
+ .name = "mmc",
+ .parent = &jz_clk_pll_half,
+ .gate_bit = JZ_CLOCK_GATE_MMC,
+ .ops = &jz_clk_divided_ops,
+ },
+ .reg = JZ_REG_CLOCK_MMC,
+ .mask = JZ_CLOCK_MMC_DIV_MASK,
+ },
+ {
+ .clk = {
+ .name = "uhc",
+ .parent = &jz_clk_pll_half,
+ .gate_bit = JZ_CLOCK_GATE_UHC,
+ .ops = &jz_clk_divided_ops,
+ },
+ .reg = JZ_REG_CLOCK_UHC,
+ .mask = JZ_CLOCK_UHC_DIV_MASK,
+ },
+};
+
+static const struct clk_ops jz_clk_udc_ops = {
+ .set_parent = jz_clk_udc_set_parent,
+ .set_rate = jz_clk_udc_set_rate,
+ .get_rate = jz_clk_udc_get_rate,
+ .enable = jz_clk_udc_enable,
+ .disable = jz_clk_udc_disable,
+ .is_enabled = jz_clk_udc_is_enabled,
+};
+
+static const struct clk_ops jz_clk_simple_ops = {
+ .enable = jz_clk_enable_gating,
+ .disable = jz_clk_disable_gating,
+ .is_enabled = jz_clk_is_enabled_gating,
+};
+
+static struct clk jz4740_clock_simple_clks[] = {
+ {
+ .name = "udc",
+ .parent = &jz_clk_ext.clk,
+ .ops = &jz_clk_udc_ops,
+ },
+ {
+ .name = "uart0",
+ .parent = &jz_clk_ext.clk,
+ .gate_bit = JZ_CLOCK_GATE_UART0,
+ .ops = &jz_clk_simple_ops,
+ },
+ {
+ .name = "uart1",
+ .parent = &jz_clk_ext.clk,
+ .gate_bit = JZ_CLOCK_GATE_UART1,
+ .ops = &jz_clk_simple_ops,
+ },
+ {
+ .name = "dma",
+ .parent = &jz_clk_high_speed_peripheral.clk,
+ .gate_bit = JZ_CLOCK_GATE_UART0,
+ .ops = &jz_clk_simple_ops,
+ },
+ {
+ .name = "ipu",
+ .parent = &jz_clk_high_speed_peripheral.clk,
+ .gate_bit = JZ_CLOCK_GATE_IPU,
+ .ops = &jz_clk_simple_ops,
+ },
+ {
+ .name = "adc",
+ .parent = &jz_clk_ext.clk,
+ .gate_bit = JZ_CLOCK_GATE_ADC,
+ .ops = &jz_clk_simple_ops,
+ },
+ {
+ .name = "i2c",
+ .parent = &jz_clk_ext.clk,
+ .gate_bit = JZ_CLOCK_GATE_I2C,
+ .ops = &jz_clk_simple_ops,
+ },
+ {
+ .name = "aic",
+ .parent = &jz_clk_ext.clk,
+ .gate_bit = JZ_CLOCK_GATE_AIC,
+ .ops = &jz_clk_simple_ops,
+ },
+};
+
+static struct static_clk jz_clk_rtc = {
+ .clk = {
+ .name = "rtc",
+ .gate_bit = JZ_CLOCK_GATE_RTC,
+ .ops = &jz_clk_static_ops,
+ },
+ .rate = 32768,
+};
+
+int clk_enable(struct clk *clk)
+{
+ if (!clk->ops->enable)
+ return -EINVAL;
+
+ return clk->ops->enable(clk);
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+ if (clk->ops->disable)
+ clk->ops->disable(clk);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+int clk_is_enabled(struct clk *clk)
+{
+ if (clk->ops->is_enabled)
+ return clk->ops->is_enabled(clk);
+
+ return 1;
+}
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (clk->ops->get_rate)
+ return clk->ops->get_rate(clk);
+ if (clk->parent)
+ return clk_get_rate(clk->parent);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ if (!clk->ops->set_rate)
+ return -EINVAL;
+ return clk->ops->set_rate(clk, rate);
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (clk->ops->round_rate)
+ return clk->ops->round_rate(clk, rate);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret;
+
+ if (!clk->ops->set_parent)
+ return -EINVAL;
+
+ clk_disable(clk);
+ ret = clk->ops->set_parent(clk, parent);
+ clk_enable(clk);
+
+ jz4740_clock_debugfs_update_parent(clk);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+struct clk *clk_get(struct device *dev, const char *name)
+{
+ struct clk *clk;
+
+ list_for_each_entry(clk, &jz_clocks, list) {
+ if (strcmp(clk->name, name) == 0)
+ return clk;
+ }
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_GPL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL_GPL(clk_put);
+
+
+static inline void clk_add(struct clk *clk)
+{
+ list_add_tail(&clk->list, &jz_clocks);
+
+ jz4740_clock_debugfs_add_clk(clk);
+}
+
+static void clk_register_clks(void)
+{
+ size_t i;
+
+ clk_add(&jz_clk_ext.clk);
+ clk_add(&jz_clk_pll);
+ clk_add(&jz_clk_pll_half);
+ clk_add(&jz_clk_cpu.clk);
+ clk_add(&jz_clk_high_speed_peripheral.clk);
+ clk_add(&jz_clk_low_speed_peripheral.clk);
+ clk_add(&jz_clk_ko);
+ clk_add(&jz_clk_ld);
+ clk_add(&jz_clk_rtc.clk);
+
+ for (i = 0; i < ARRAY_SIZE(jz4740_clock_divided_clks); ++i)
+ clk_add(&jz4740_clock_divided_clks[i].clk);
+
+ for (i = 0; i < ARRAY_SIZE(jz4740_clock_simple_clks); ++i)
+ clk_add(&jz4740_clock_simple_clks[i]);
+}
+
+void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode)
+{
+ switch (mode) {
+ case JZ4740_WAIT_MODE_IDLE:
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_LOW_POWER, JZ_CLOCK_LOW_POWER_MODE_SLEEP);
+ break;
+ case JZ4740_WAIT_MODE_SLEEP:
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_LOW_POWER, JZ_CLOCK_LOW_POWER_MODE_SLEEP);
+ break;
+ }
+}
+
+void jz4740_clock_udc_disable_auto_suspend(void)
+{
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_GATE, JZ_CLOCK_GATE_UDC);
+}
+EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend);
+
+void jz4740_clock_udc_enable_auto_suspend(void)
+{
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_GATE, JZ_CLOCK_GATE_UDC);
+}
+EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend);
+
+void jz4740_clock_suspend(void)
+{
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_GATE,
+ JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0);
+
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_PLL, JZ_CLOCK_PLL_ENABLED);
+}
+
+void jz4740_clock_resume(void)
+{
+ uint32_t pll;
+
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_PLL, JZ_CLOCK_PLL_ENABLED);
+
+ do {
+ pll = jz_clk_reg_read(JZ_REG_CLOCK_PLL);
+ } while (!(pll & JZ_CLOCK_PLL_STABLE));
+
+ jz_clk_reg_clear_bits(JZ_REG_CLOCK_GATE,
+ JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0);
+}
+
+static int jz4740_clock_init(void)
+{
+ uint32_t val;
+
+ jz_clock_base = ioremap(CPHYSADDR(JZ4740_CPM_BASE_ADDR), 0x100);
+ if (!jz_clock_base)
+ return -EBUSY;
+
+ spin_lock_init(&jz_clock_lock);
+
+ jz_clk_ext.rate = jz4740_clock_bdata.ext_rate;
+ jz_clk_rtc.rate = jz4740_clock_bdata.rtc_rate;
+
+ val = jz_clk_reg_read(JZ_REG_CLOCK_SPI);
+
+ if (val & JZ_CLOCK_SPI_SRC_PLL)
+ jz4740_clock_divided_clks[1].clk.parent = &jz_clk_pll_half;
+
+ val = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
+
+ if (val & JZ_CLOCK_CTRL_I2S_SRC_PLL)
+ jz4740_clock_divided_clks[0].clk.parent = &jz_clk_pll_half;
+
+ if (val & JZ_CLOCK_CTRL_UDC_SRC_PLL)
+ jz4740_clock_simple_clks[0].parent = &jz_clk_pll_half;
+
+ jz4740_clock_debugfs_init();
+
+ clk_register_clks();
+
+ return 0;
+}
+subsys_initcall(jz4740_clock_init);
diff --git a/arch/mips/jz4740/clock.h b/arch/mips/jz4740/clock.h
new file mode 100644
index 0000000..5d07499
--- /dev/null
+++ b/arch/mips/jz4740/clock.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC clock support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __MIPS_JZ4740_CLOCK_H__
+#define __MIPS_JZ4740_CLOCK_H__
+
+#include <linux/list.h>
+
+struct jz4740_clock_board_data {
+ unsigned long ext_rate;
+ unsigned long rtc_rate;
+};
+
+extern struct jz4740_clock_board_data jz4740_clock_bdata;
+
+void jz4740_clock_suspend(void);
+void jz4740_clock_resume(void);
+
+struct clk;
+
+struct clk_ops {
+ unsigned long (*get_rate)(struct clk *clk);
+ unsigned long (*round_rate)(struct clk *clk, unsigned long rate);
+ int (*set_rate)(struct clk *clk, unsigned long rate);
+ int (*enable)(struct clk *clk);
+ int (*disable)(struct clk *clk);
+ int (*is_enabled)(struct clk *clk);
+
+ int (*set_parent)(struct clk *clk, struct clk *parent);
+
+};
+
+struct clk {
+ const char *name;
+ struct clk *parent;
+
+ uint32_t gate_bit;
+
+ const struct clk_ops *ops;
+
+ struct list_head list;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_entry;
+ struct dentry *debugfs_parent_entry;
+#endif
+
+};
+
+#define JZ4740_CLK_NOT_GATED ((uint32_t)-1)
+
+int clk_is_enabled(struct clk *clk);
+
+#ifdef CONFIG_DEBUG_FS
+void jz4740_clock_debugfs_init(void);
+void jz4740_clock_debugfs_add_clk(struct clk *clk);
+void jz4740_clock_debugfs_update_parent(struct clk *clk);
+#else
+static inline void jz4740_clock_debugfs_init(void) {};
+static inline void jz4740_clock_debugfs_add_clk(struct clk *clk) {};
+static inline void jz4740_clock_debugfs_update_parent(struct clk *clk) {};
+#endif
+
+#endif
--
1.5.6.5
This patch adds support for the timer/counter unit on a JZ4740 SoC.
This code is used as a common base for the JZ4740 clocksource/clockevent
implementation and PWM support.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/include/asm/mach-jz4740/timer.h | 22 +++++
arch/mips/jz4740/timer.c | 48 ++++++++++
arch/mips/jz4740/timer.h | 136 +++++++++++++++++++++++++++++
3 files changed, 206 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/timer.h
create mode 100644 arch/mips/jz4740/timer.c
create mode 100644 arch/mips/jz4740/timer.h
diff --git a/arch/mips/include/asm/mach-jz4740/timer.h b/arch/mips/include/asm/mach-jz4740/timer.h
new file mode 100644
index 0000000..eadbf70
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/timer.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform timer support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __ASM_MACH_JZ4740_TIMER
+#define __ASM_MACH_JZ4740_TIMER
+
+void jz4740_timer_enable_watchdog(void);
+void jz4740_timer_disable_watchdog(void);
+
+#endif
diff --git a/arch/mips/jz4740/timer.c b/arch/mips/jz4740/timer.c
new file mode 100644
index 0000000..6e09cae
--- /dev/null
+++ b/arch/mips/jz4740/timer.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform timer support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "timer.h"
+
+#include <asm/mach-jz4740/base.h>
+
+void __iomem *jz4740_timer_base;
+
+void jz4740_timer_enable_watchdog(void)
+{
+ writel(BIT(16), jz4740_timer_base + JZ_REG_TIMER_STOP_CLEAR);
+}
+
+void jz4740_timer_disable_watchdog(void)
+{
+ writel(BIT(16), jz4740_timer_base + JZ_REG_TIMER_STOP_SET);
+}
+
+void __init jz4740_timer_init(void)
+{
+ jz4740_timer_base = ioremap(CPHYSADDR(JZ4740_TCU_BASE_ADDR), 0x100);
+
+ if (!jz4740_timer_base)
+ panic("Failed to ioremap timer registers");
+
+ /* Disable all timer clocks except for those used as system timers */
+ writel(0x000100fc, jz4740_timer_base + JZ_REG_TIMER_STOP_SET);
+
+ /* Timer irqs are unmasked by default, mask them */
+ writel(0x00ff00ff, jz4740_timer_base + JZ_REG_TIMER_MASK_SET);
+}
diff --git a/arch/mips/jz4740/timer.h b/arch/mips/jz4740/timer.h
new file mode 100644
index 0000000..1dacc3b
--- /dev/null
+++ b/arch/mips/jz4740/timer.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform timer support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __MIPS_JZ4740_TIMER_H__
+#define __MIPS_JZ4740_TIMER_H__
+
+#include <linux/module.h>
+#include <linux/io.h>
+
+#define JZ_REG_TIMER_STOP 0x1C
+#define JZ_REG_TIMER_STOP_SET 0x2C
+#define JZ_REG_TIMER_STOP_CLEAR 0x3C
+#define JZ_REG_TIMER_ENABLE 0x10
+#define JZ_REG_TIMER_ENABLE_SET 0x14
+#define JZ_REG_TIMER_ENABLE_CLEAR 0x18
+#define JZ_REG_TIMER_FLAG 0x20
+#define JZ_REG_TIMER_FLAG_SET 0x24
+#define JZ_REG_TIMER_FLAG_CLEAR 0x28
+#define JZ_REG_TIMER_MASK 0x30
+#define JZ_REG_TIMER_MASK_SET 0x34
+#define JZ_REG_TIMER_MASK_CLEAR 0x38
+
+#define JZ_REG_TIMER_DFR(x) (((x) * 0x10) + 0x40)
+#define JZ_REG_TIMER_DHR(x) (((x) * 0x10) + 0x44)
+#define JZ_REG_TIMER_CNT(x) (((x) * 0x10) + 0x48)
+#define JZ_REG_TIMER_CTRL(x) (((x) * 0x10) + 0x4C)
+
+#define JZ_TIMER_IRQ_HALF(x) BIT((x) + 0x10)
+#define JZ_TIMER_IRQ_FULL(x) BIT(x)
+
+#define JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN BIT(9)
+#define JZ_TIMER_CTRL_PWM_ACTIVE_LOW BIT(8)
+#define JZ_TIMER_CTRL_PWM_ENABLE BIT(7)
+#define JZ_TIMER_CTRL_PRESCALE_MASK 0x1c
+#define JZ_TIMER_CTRL_PRESCALE_OFFSET 0x3
+#define JZ_TIMER_CTRL_PRESCALE_1 (0 << 3)
+#define JZ_TIMER_CTRL_PRESCALE_4 (1 << 3)
+#define JZ_TIMER_CTRL_PRESCALE_16 (2 << 3)
+#define JZ_TIMER_CTRL_PRESCALE_64 (3 << 3)
+#define JZ_TIMER_CTRL_PRESCALE_256 (4 << 3)
+#define JZ_TIMER_CTRL_PRESCALE_1024 (5 << 3)
+
+#define JZ_TIMER_CTRL_PRESCALER(x) ((x) << JZ_TIMER_CTRL_PRESCALE_OFFSET)
+
+#define JZ_TIMER_CTRL_SRC_EXT BIT(2)
+#define JZ_TIMER_CTRL_SRC_RTC BIT(1)
+#define JZ_TIMER_CTRL_SRC_PCLK BIT(0)
+
+extern void __iomem *jz4740_timer_base;
+void __init jz4740_timer_init(void);
+
+static inline void jz4740_timer_stop(unsigned int timer)
+{
+ writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_SET);
+}
+
+static inline void jz4740_timer_start(unsigned int timer)
+{
+ writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_CLEAR);
+}
+
+static inline bool jz4740_timer_is_enabled(unsigned int timer)
+{
+ return readb(jz4740_timer_base + JZ_REG_TIMER_ENABLE) & BIT(timer);
+}
+
+static inline void jz4740_timer_enable(unsigned int timer)
+{
+ writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_SET);
+}
+
+static inline void jz4740_timer_disable(unsigned int timer)
+{
+ writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_CLEAR);
+}
+
+
+static inline void jz4740_timer_set_period(unsigned int timer, uint16_t period)
+{
+ writew(period, jz4740_timer_base + JZ_REG_TIMER_DFR(timer));
+}
+
+static inline void jz4740_timer_set_duty(unsigned int timer, uint16_t duty)
+{
+ writew(duty, jz4740_timer_base + JZ_REG_TIMER_DHR(timer));
+}
+
+static inline void jz4740_timer_set_count(unsigned int timer, uint16_t count)
+{
+ writew(count, jz4740_timer_base + JZ_REG_TIMER_CNT(timer));
+}
+
+static inline uint16_t jz4740_timer_get_count(unsigned int timer)
+{
+ return readw(jz4740_timer_base + JZ_REG_TIMER_CNT(timer));
+}
+
+static inline void jz4740_timer_ack_full(unsigned int timer)
+{
+ writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR);
+}
+
+static inline void jz4740_timer_irq_full_enable(unsigned int timer)
+{
+ writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR);
+ writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_CLEAR);
+}
+
+static inline void jz4740_timer_irq_full_disable(unsigned int timer)
+{
+ writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_SET);
+}
+
+static inline void jz4740_timer_set_ctrl(unsigned int timer, uint16_t ctrl)
+{
+ writew(ctrl, jz4740_timer_base + JZ_REG_TIMER_CTRL(timer));
+}
+
+static inline uint16_t jz4740_timer_get_ctrl(unsigned int timer)
+{
+ return readw(jz4740_timer_base + JZ_REG_TIMER_CTRL(timer));
+}
+
+#endif
--
1.5.6.5
This patch adds plat_mem_setup and get_system_type for JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/jz4740/setup.c | 33 +++++++++++++++++++++++++++++++++
1 files changed, 33 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/setup.c
diff --git a/arch/mips/jz4740/setup.c b/arch/mips/jz4740/setup.c
new file mode 100644
index 0000000..d449358
--- /dev/null
+++ b/arch/mips/jz4740/setup.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 setup code
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <asm/mach-jz4740/base.h>
+#include <asm/mach-jz4740/clock.h>
+
+#include "reset.h"
+#include "clock.h"
+
+void __init plat_mem_setup(void)
+{
+ jz4740_reset_init();
+}
+
+const char *get_system_type(void)
+{
+ return "JZ4740";
+}
--
1.5.6.5
This patch adds support for DMA transfers on JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/include/asm/mach-jz4740/dma.h | 90 ++++++++++
arch/mips/jz4740/dma.c | 289 +++++++++++++++++++++++++++++++
2 files changed, 379 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/dma.h
create mode 100644 arch/mips/jz4740/dma.c
diff --git a/arch/mips/include/asm/mach-jz4740/dma.h b/arch/mips/include/asm/mach-jz4740/dma.h
new file mode 100644
index 0000000..bb7fc1e
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/dma.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ7420/JZ4740 DMA definitions
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __ASM_MACH_JZ4740_DMA_H__
+#define __ASM_MACH_JZ4740_DMA_H__
+
+struct jz4740_dma_chan;
+
+enum jz4740_dma_request_type {
+ JZ4740_DMA_TYPE_AUTO_REQUEST = 8,
+ JZ4740_DMA_TYPE_UART_TRANSMIT = 20,
+ JZ4740_DMA_TYPE_UART_RECEIVE = 21,
+ JZ4740_DMA_TYPE_SPI_TRANSMIT = 22,
+ JZ4740_DMA_TYPE_SPI_RECEIVE = 23,
+ JZ4740_DMA_TYPE_AIC_TRANSMIT = 24,
+ JZ4740_DMA_TYPE_AIC_RECEIVE = 25,
+ JZ4740_DMA_TYPE_MMC_TRANSMIT = 26,
+ JZ4740_DMA_TYPE_MMC_RECEIVE = 27,
+ JZ4740_DMA_TYPE_TCU = 28,
+ JZ4740_DMA_TYPE_SADC = 29,
+ JZ4740_DMA_TYPE_SLCD = 30,
+};
+
+enum jz4740_dma_width {
+ JZ4740_DMA_WIDTH_32BIT = 0,
+ JZ4740_DMA_WIDTH_8BIT = 1,
+ JZ4740_DMA_WIDTH_16BIT = 2,
+};
+
+enum jz4740_dma_transfer_size {
+ JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0,
+ JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1,
+ JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2,
+ JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3,
+ JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4,
+};
+
+enum jz4740_dma_flags {
+ JZ4740_DMA_SRC_AUTOINC = 0x2,
+ JZ4740_DMA_DST_AUTOINC = 0x1,
+};
+
+enum jz4740_dma_mode {
+ JZ4740_DMA_MODE_SINGLE = 0,
+ JZ4740_DMA_MODE_BLOCK = 1,
+};
+
+struct jz4740_dma_config {
+ enum jz4740_dma_width src_width;
+ enum jz4740_dma_width dst_width;
+ enum jz4740_dma_transfer_size transfer_size;
+ enum jz4740_dma_request_type request_type;
+ enum jz4740_dma_flags flags;
+ enum jz4740_dma_mode mode;
+};
+
+typedef void (*jz4740_dma_complete_callback_t)(struct jz4740_dma_chan *, int, void *);
+
+struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name);
+void jz4740_dma_free(struct jz4740_dma_chan *dma);
+
+void jz4740_dma_configure(struct jz4740_dma_chan *dma,
+ const struct jz4740_dma_config *config);
+
+
+void jz4740_dma_enable(struct jz4740_dma_chan *dma);
+void jz4740_dma_disable(struct jz4740_dma_chan *dma);
+
+void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src);
+void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst);
+void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count);
+
+uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma);
+
+void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
+ jz4740_dma_complete_callback_t cb);
+
+#endif /* __ASM_JZ4740_DMA_H__ */
diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c
new file mode 100644
index 0000000..4016e62
--- /dev/null
+++ b/arch/mips/jz4740/dma.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC DMA support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+#include <linux/dma-mapping.h>
+#include <asm/mach-jz4740/dma.h>
+#include <asm/mach-jz4740/base.h>
+
+#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20)
+#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20)
+#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20)
+#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20)
+#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20)
+#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20)
+#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20)
+
+#define JZ_REG_DMA_CTRL 0x300
+#define JZ_REG_DMA_IRQ 0x304
+#define JZ_REG_DMA_DOORBELL 0x308
+#define JZ_REG_DMA_DOORBELL_SET 0x30C
+
+#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31)
+#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6)
+#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4)
+#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3)
+#define JZ_DMA_STATUS_CTRL_HALT BIT(2)
+#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1)
+#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0)
+
+#define JZ_DMA_CMD_SRC_INC BIT(23)
+#define JZ_DMA_CMD_DST_INC BIT(22)
+#define JZ_DMA_CMD_RDIL_MASK (0xf << 16)
+#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14)
+#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12)
+#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8)
+#define JZ_DMA_CMD_BLOCK_MODE BIT(7)
+#define JZ_DMA_CMD_DESC_VALID BIT(4)
+#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3)
+#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2)
+#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1)
+#define JZ_DMA_CMD_LINK_ENABLE BIT(0)
+
+#define JZ_DMA_CMD_FLAGS_OFFSET 22
+#define JZ_DMA_CMD_RDIL_OFFSET 16
+#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14
+#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12
+#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8
+#define JZ_DMA_CMD_MODE_OFFSET 7
+
+#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8)
+#define JZ_DMA_CTRL_HALT BIT(3)
+#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2)
+#define JZ_DMA_CTRL_ENABLE BIT(0)
+
+
+static void __iomem *jz4740_dma_base;
+static spinlock_t jz4740_dma_lock;
+
+static inline uint32_t jz4740_dma_read(size_t reg)
+{
+ return readl(jz4740_dma_base + reg);
+}
+
+static inline void jz4740_dma_write(size_t reg, uint32_t val)
+{
+ writel(val, jz4740_dma_base + reg);
+}
+
+static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask)
+{
+ uint32_t val2;
+ val2 = jz4740_dma_read(reg);
+ val2 &= ~mask;
+ val2 |= val;
+ jz4740_dma_write(reg, val2);
+}
+
+struct jz4740_dma_chan {
+ unsigned int id;
+ void *dev;
+ const char *name;
+
+ enum jz4740_dma_flags flags;
+ uint32_t transfer_shift;
+
+ jz4740_dma_complete_callback_t complete_cb;
+
+ unsigned used:1;
+};
+
+#define JZ4740_DMA_CHANNEL(_id) { .id = _id }
+
+struct jz4740_dma_chan jz4740_dma_channels[] = {
+ JZ4740_DMA_CHANNEL(0),
+ JZ4740_DMA_CHANNEL(1),
+ JZ4740_DMA_CHANNEL(2),
+ JZ4740_DMA_CHANNEL(3),
+ JZ4740_DMA_CHANNEL(4),
+ JZ4740_DMA_CHANNEL(5),
+};
+
+struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name)
+{
+ unsigned int i;
+ struct jz4740_dma_chan *dma = NULL;
+
+ spin_lock(&jz4740_dma_lock);
+
+ for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) {
+ if (!jz4740_dma_channels[i].used) {
+ dma = &jz4740_dma_channels[i];
+ dma->used = 1;
+ break;
+ }
+ }
+
+ spin_unlock(&jz4740_dma_lock);
+
+ if (!dma)
+ return NULL;
+
+ dma->dev = dev;
+ dma->name = name;
+
+ return dma;
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_request);
+
+void jz4740_dma_configure(struct jz4740_dma_chan *dma,
+ const struct jz4740_dma_config *config)
+{
+ uint32_t cmd;
+
+ switch (config->transfer_size) {
+ case JZ4740_DMA_TRANSFER_SIZE_2BYTE:
+ dma->transfer_shift = 1;
+ break;
+ case JZ4740_DMA_TRANSFER_SIZE_4BYTE:
+ dma->transfer_shift = 2;
+ break;
+ case JZ4740_DMA_TRANSFER_SIZE_16BYTE:
+ dma->transfer_shift = 4;
+ break;
+ case JZ4740_DMA_TRANSFER_SIZE_32BYTE:
+ dma->transfer_shift = 5;
+ break;
+ default:
+ dma->transfer_shift = 0;
+ break;
+ }
+
+ cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET;
+ cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET;
+ cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET;
+ cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET;
+ cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET;
+ cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
+
+ jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd);
+ jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0);
+ jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type);
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_configure);
+
+void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src)
+{
+ jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src);
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr);
+
+void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst)
+{
+ jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst);
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr);
+
+void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count)
+{
+ count >>= dma->transfer_shift;
+ jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count);
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count);
+
+void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
+ jz4740_dma_complete_callback_t cb)
+{
+ dma->complete_cb = cb;
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb);
+
+void jz4740_dma_free(struct jz4740_dma_chan *dma)
+{
+ dma->dev = NULL;
+ dma->complete_cb = NULL;
+ dma->used = 0;
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_free);
+
+void jz4740_dma_enable(struct jz4740_dma_chan *dma)
+{
+ jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id),
+ JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE,
+ JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC |
+ JZ_DMA_STATUS_CTRL_ENABLE);
+
+ jz4740_dma_write_mask(JZ_REG_DMA_CTRL,
+ JZ_DMA_CTRL_ENABLE,
+ JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE);
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_enable);
+
+void jz4740_dma_disable(struct jz4740_dma_chan *dma)
+{
+ jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
+ JZ_DMA_STATUS_CTRL_ENABLE);
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_disable);
+
+uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma)
+{
+ uint32_t residue;
+ residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id));
+ return residue << dma->transfer_shift;
+}
+EXPORT_SYMBOL_GPL(jz4740_dma_get_residue);
+
+static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma)
+{
+ uint32_t status;
+
+ status = jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id));
+
+ jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
+ JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE);
+
+ if (dma->complete_cb)
+ dma->complete_cb(dma, 0, dma->dev);
+}
+
+static irqreturn_t jz4740_dma_irq(int irq, void *dev_id)
+{
+ uint32_t irq_status;
+ unsigned int i;
+
+ irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ);
+
+ for (i = 0; i < 6; ++i) {
+ if (irq_status & (1 << i))
+ jz4740_dma_chan_irq(&jz4740_dma_channels[i]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int jz4740_dma_init(void)
+{
+ unsigned int ret;
+
+ jz4740_dma_base = ioremap(CPHYSADDR(JZ4740_DMAC_BASE_ADDR), 0x400);
+
+ if (!jz4740_dma_base)
+ return -EBUSY;
+
+ spin_lock_init(&jz4740_dma_lock);
+
+ ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL);
+
+ if (ret)
+ printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret);
+
+ return ret;
+}
+arch_initcall(jz4740_dma_init);
--
1.5.6.5
This patch adds support for initializing arcs_cmdline on JZ4740 based machines
and provides a prom_putchar implementation.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/jz4740/prom.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 69 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/prom.c
diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c
new file mode 100644
index 0000000..6518aee
--- /dev/null
+++ b/arch/mips/jz4740/prom.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC prom code
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <linux/serial_reg.h>
+
+#include <asm/bootinfo.h>
+#include <asm/mach-jz4740/base.h>
+
+void jz4740_init_cmdline(int argc, char *argv[])
+{
+ unsigned int count = COMMAND_LINE_SIZE - 1;
+ int i;
+ char *dst = &(arcs_cmdline[0]);
+ char *src;
+
+ for (i = 1; i < argc && count; ++i) {
+ src = argv[i];
+ while (*src && count) {
+ *dst++ = *src++;
+ --count;
+ }
+ *dst++ = ' ';
+ }
+ if (i > 1)
+ --dst;
+
+ *dst = 0;
+}
+
+void __init prom_init(void)
+{
+ jz4740_init_cmdline((int)fw_arg0, (char **)fw_arg1);
+ mips_machtype = MACH_INGENIC_JZ4740;
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
+
+#define UART_REG(_reg) ((void __iomem *)(JZ4740_UART0_BASE_ADDR + (_reg << 2)))
+
+void prom_putchar(char c)
+{
+ uint8_t lsr;
+
+ do {
+ lsr = readb(UART_REG(UART_LSR));
+ } while ((lsr & UART_LSR_TEMT) == 0);
+
+ writeb(c, UART_REG(UART_TX));
+}
--
1.5.6.5
This patch adds platform devices for all the JZ4740 platform drivers.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/include/asm/mach-jz4740/platform.h | 36 ++++
arch/mips/jz4740/platform.c | 288 ++++++++++++++++++++++++++
2 files changed, 324 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/platform.h
create mode 100644 arch/mips/jz4740/platform.c
diff --git a/arch/mips/include/asm/mach-jz4740/platform.h b/arch/mips/include/asm/mach-jz4740/platform.h
new file mode 100644
index 0000000..7a51c70
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/platform.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform device definitions
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef __JZ4740_PLATFORM_H
+#define __JZ4740_PLATFORM_H
+
+#include <linux/platform_device.h>
+
+extern struct platform_device jz4740_usb_ohci_device;
+extern struct platform_device jz4740_udc_device;
+extern struct platform_device jz4740_mmc_device;
+extern struct platform_device jz4740_rtc_device;
+extern struct platform_device jz4740_i2c_device;
+extern struct platform_device jz4740_nand_device;
+extern struct platform_device jz4740_framebuffer_device;
+extern struct platform_device jz4740_i2s_device;
+extern struct platform_device jz4740_codec_device;
+extern struct platform_device jz4740_adc_device;
+extern struct platform_device jz4740_battery_device;
+
+void jz4740_serial_device_register(void);
+
+#endif
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c
new file mode 100644
index 0000000..734b441
--- /dev/null
+++ b/arch/mips/jz4740/platform.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform devices
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/resource.h>
+
+#include <asm/mach-jz4740/platform.h>
+#include <asm/mach-jz4740/base.h>
+#include <asm/mach-jz4740/irq.h>
+
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+
+#include "serial.h"
+#include "clock.h"
+
+/* OHCI (USB full speed host controller) */
+static struct resource jz4740_usb_ohci_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_UHC_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_UHC_BASE_ADDR) + 0x10000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = JZ4740_IRQ_UHC,
+ .end = JZ4740_IRQ_UHC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/* The dmamask must be set for OHCI to work */
+static u64 ohci_dmamask = ~(u32)0;
+
+struct platform_device jz4740_usb_ohci_device = {
+ .name = "jz4740-ohci",
+ .id = -1,
+ .dev = {
+ .dma_mask = &ohci_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .num_resources = ARRAY_SIZE(jz4740_usb_ohci_resources),
+ .resource = jz4740_usb_ohci_resources,
+};
+
+/* UDC (USB gadget controller) */
+static struct resource jz4740_usb_gdt_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_UDC_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_UDC_BASE_ADDR) + 0x10000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = JZ4740_IRQ_UDC,
+ .end = JZ4740_IRQ_UDC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 jz4740_udc_dmamask = ~(u32)0;
+
+struct platform_device jz4740_udc_device = {
+ .name = "jz-udc",
+ .id = -1,
+ .dev = {
+ .dma_mask = &jz4740_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .num_resources = ARRAY_SIZE(jz4740_usb_gdt_resources),
+ .resource = jz4740_usb_gdt_resources,
+};
+
+/** MMC/SD controller **/
+static struct resource jz4740_mmc_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_MSC_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_MSC_BASE_ADDR) + 0x10000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = JZ4740_IRQ_MSC,
+ .end = JZ4740_IRQ_MSC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 jz4740_mmc_dmamask = ~(u32)0;
+
+struct platform_device jz4740_mmc_device = {
+ .name = "jz4740-mmc",
+ .id = 0,
+ .dev = {
+ .dma_mask = &jz4740_mmc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .num_resources = ARRAY_SIZE(jz4740_mmc_resources),
+ .resource = jz4740_mmc_resources,
+};
+
+static struct resource jz4740_rtc_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_RTC_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_RTC_BASE_ADDR) + 0x38 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = JZ4740_IRQ_RTC,
+ .end = JZ4740_IRQ_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device jz4740_rtc_device = {
+ .name = "jz4740-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(jz4740_rtc_resources),
+ .resource = jz4740_rtc_resources,
+};
+
+/** I2C controller **/
+static struct resource jz4740_i2c_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_I2C_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_I2C_BASE_ADDR) + 0x10000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = JZ4740_IRQ_I2C,
+ .end = JZ4740_IRQ_I2C,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 jz4740_i2c_dmamask = ~(u32)0;
+
+struct platform_device jz4740_i2c_device = {
+ .name = "jz_i2c",
+ .id = 0,
+ .dev = {
+ .dma_mask = &jz4740_i2c_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .num_resources = ARRAY_SIZE(jz4740_i2c_resources),
+ .resource = jz4740_i2c_resources,
+};
+
+static struct resource jz4740_nand_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_EMC_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_EMC_BASE_ADDR) + 0x10000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device jz4740_nand_device = {
+ .name = "jz4740-nand",
+ .num_resources = ARRAY_SIZE(jz4740_nand_resources),
+ .resource = jz4740_nand_resources,
+};
+
+static struct resource jz4740_framebuffer_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_LCD_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_LCD_BASE_ADDR) + 0x10000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static u64 jz4740_fb_dmamask = ~(u32)0;
+
+struct platform_device jz4740_framebuffer_device = {
+ .name = "jz4740-fb",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(jz4740_framebuffer_resources),
+ .resource = jz4740_framebuffer_resources,
+ .dev = {
+ .dma_mask = &jz4740_fb_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+static struct resource jz4740_i2s_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_AIC_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_AIC_BASE_ADDR) + 0x38 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device jz4740_i2s_device = {
+ .name = "jz4740-i2s",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(jz4740_i2s_resources),
+ .resource = jz4740_i2s_resources,
+};
+
+static struct resource jz4740_codec_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_AIC_BASE_ADDR) + 0x80,
+ .end = CPHYSADDR(JZ4740_AIC_BASE_ADDR) + 0x88 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device jz4740_codec_device = {
+ .name = "jz4740-codec",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(jz4740_codec_resources),
+ .resource = jz4740_codec_resources,
+};
+
+static struct resource jz4740_adc_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(JZ4740_SADC_BASE_ADDR),
+ .end = CPHYSADDR(JZ4740_SADC_BASE_ADDR) + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = JZ4740_IRQ_SADC,
+ .end = JZ4740_IRQ_SADC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device jz4740_adc_device = {
+ .name = "jz4740-adc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(jz4740_adc_resources),
+ .resource = jz4740_adc_resources,
+};
+
+struct platform_device jz4740_battery_device = {
+ .name = "jz4740-battery",
+ .id = -1,
+ .dev = {
+ .parent = &jz4740_adc_device.dev
+ },
+};
+
+/* Serial */
+#define JZ4740_UART_DATA(_id) \
+ { \
+ .flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE, \
+ .iotype = UPIO_MEM, \
+ .regshift = 2, \
+ .serial_out = jz4740_serial_out, \
+ .type = PORT_16550A, \
+ .mapbase = CPHYSADDR(JZ4740_UART ## _id ## _BASE_ADDR), \
+ .irq = JZ4740_IRQ_UART ## _id, \
+ }
+
+static struct plat_serial8250_port jz4740_uart_data[] = {
+ JZ4740_UART_DATA(0),
+ JZ4740_UART_DATA(1),
+ {},
+};
+
+static struct platform_device jz4740_uart_device = {
+ .name = "serial8250",
+ .id = 0,
+ .dev = {
+ .platform_data = jz4740_uart_data,
+ },
+};
+
+void jz4740_serial_device_register(void)
+{
+ struct plat_serial8250_port *p;
+
+ for (p = jz4740_uart_data; p->flags != 0; ++p)
+ p->uartclk = jz4740_clock_bdata.ext_rate;
+
+ platform_device_register(&jz4740_uart_device);
+}
--
1.5.6.5
This patch adds support for the RTC unit on JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Alessandro Zummo <[email protected]>
Cc: Paul Gortmaker <[email protected]>
Cc: [email protected]
---
drivers/rtc/Kconfig | 11 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-jz4740.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 356 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-jz4740.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 10ba12c..d0ed7e6 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -905,4 +905,15 @@ config RTC_DRV_MPC5121
This driver can also be built as a module. If so, the module
will be called rtc-mpc5121.
+config RTC_DRV_JZ4740
+ tristate "Ingenic JZ4740 SoC"
+ depends on RTC_CLASS
+ depends on MACH_JZ4740
+ help
+ If you say yes here you get support for the Ingenic JZ4740 SoC RTC
+ controller.
+
+ This driver can also be buillt as a module. If so, the module
+ will be called rtc-jz4740.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 5adbba7..fedf9bb 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
+obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
new file mode 100644
index 0000000..41ab78f
--- /dev/null
+++ b/drivers/rtc/rtc-jz4740.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC RTC driver
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define JZ_REG_RTC_CTRL 0x00
+#define JZ_REG_RTC_SEC 0x04
+#define JZ_REG_RTC_SEC_ALARM 0x08
+#define JZ_REG_RTC_REGULATOR 0x0C
+#define JZ_REG_RTC_HIBERNATE 0x20
+#define JZ_REG_RTC_SCRATCHPAD 0x34
+
+#define JZ_RTC_CTRL_WRDY BIT(7)
+#define JZ_RTC_CTRL_1HZ BIT(6)
+#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
+#define JZ_RTC_CTRL_AF BIT(4)
+#define JZ_RTC_CTRL_AF_IRQ BIT(3)
+#define JZ_RTC_CTRL_AE BIT(2)
+#define JZ_RTC_CTRL_ENABLE BIT(0)
+
+struct jz4740_rtc {
+ struct resource *mem;
+ void __iomem *base;
+
+ struct rtc_device *rtc;
+
+ unsigned int irq;
+
+ spinlock_t lock;
+};
+
+static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
+{
+ return readl(rtc->base + reg);
+}
+
+static inline void jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
+{
+ uint32_t ctrl;
+ do {
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+ } while (!(ctrl & JZ_RTC_CTRL_WRDY));
+}
+
+
+static inline void jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
+ uint32_t val)
+{
+ jz4740_rtc_wait_write_ready(rtc);
+ writel(val, rtc->base + reg);
+}
+
+static void jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask,
+ uint32_t val)
+{
+ unsigned long flags;
+ uint32_t ctrl;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ /* Don't clear interrupt flags by accident */
+ ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF;
+
+ ctrl &= ~mask;
+ ctrl |= val;
+
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+static inline struct jz4740_rtc *dev_to_rtc(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct jz4740_rtc *rtc = dev_to_rtc(dev);
+ uint32_t secs, secs2;
+
+ secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+
+ while (secs != secs2) {
+ secs = secs2;
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ }
+
+ rtc_time_to_tm(secs, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct jz4740_rtc *rtc = dev_to_rtc(dev);
+
+ if ((uint32_t)secs != secs)
+ return -EINVAL;
+
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs);
+
+ return 0;
+}
+
+static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct jz4740_rtc *rtc = dev_to_rtc(dev);
+ uint32_t secs, secs2;
+ uint32_t ctrl;
+
+ secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
+
+ while (secs != secs2) {
+ secs = secs2;
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
+ }
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE);
+ alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF);
+
+ rtc_time_to_tm(secs, &alrm->time);
+
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct jz4740_rtc *rtc = dev_to_rtc(dev);
+ unsigned long secs;
+
+ rtc_tm_to_time(&alrm->time, &secs);
+
+ if ((uint32_t)secs != secs)
+ return -EINVAL;
+
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, (uint32_t)secs);
+ jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE,
+ alrm->enabled ? JZ_RTC_CTRL_AE : 0);
+
+ return 0;
+}
+
+static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct jz4740_rtc *rtc = dev_to_rtc(dev);
+ jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ,
+ enable ? JZ_RTC_CTRL_1HZ_IRQ : 0);
+ return 0;
+}
+
+
+static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct jz4740_rtc *rtc = dev_to_rtc(dev);
+ jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ,
+ enable ? JZ_RTC_CTRL_AF_IRQ : 0);
+ return 0;
+}
+
+static struct rtc_class_ops jz4740_rtc_ops = {
+ .read_time = jz4740_rtc_read_time,
+ .set_mmss = jz4740_rtc_set_mmss,
+ .read_alarm = jz4740_rtc_read_alarm,
+ .set_alarm = jz4740_rtc_set_alarm,
+ .update_irq_enable = jz4740_rtc_update_irq_enable,
+ .alarm_irq_enable = jz4740_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t jz4740_rtc_irq(int irq, void *data)
+{
+ struct jz4740_rtc *rtc = data;
+ uint32_t ctrl;
+ unsigned long events = 0;
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ if (ctrl & JZ_RTC_CTRL_1HZ)
+ events |= (RTC_UF | RTC_IRQF);
+
+ if (ctrl & JZ_RTC_CTRL_AF)
+ events |= (RTC_AF | RTC_IRQF);
+
+ rtc_update_irq(rtc->rtc, 1, events);
+
+ jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, 0);
+
+ return IRQ_HANDLED;
+}
+
+void jz4740_rtc_poweroff(struct device *dev)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1);
+}
+EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff);
+
+static int __devinit jz4740_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_rtc *rtc;
+ uint32_t scratchpad;
+
+ rtc = kmalloc(sizeof(*rtc), GFP_KERNEL);
+
+ rtc->irq = platform_get_irq(pdev, 0);
+
+ if (rtc->irq < 0) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform irq\n");
+ goto err_free;
+ }
+
+ rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!rtc->mem) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform mmio memory\n");
+ goto err_free;
+ }
+
+ rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem),
+ pdev->name);
+
+ if (!rtc->mem) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ goto err_free;
+ }
+
+ rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem));
+
+ if (!rtc->base) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+ goto err_release_mem_region;
+ }
+
+ spin_lock_init(&rtc->lock);
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret);
+ goto err_iounmap;
+ }
+
+ ret = request_irq(rtc->irq, jz4740_rtc_irq, 0,
+ pdev->name, rtc);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret);
+ goto err_unregister_rtc;
+ }
+
+ scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD);
+ if (scratchpad != 0x12345678) {
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
+ }
+
+ return 0;
+
+err_unregister_rtc:
+ rtc_device_unregister(rtc->rtc);
+err_iounmap:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(rtc->base);
+err_release_mem_region:
+ release_mem_region(rtc->mem->start, resource_size(rtc->mem));
+err_free:
+ kfree(rtc);
+
+ return ret;
+}
+
+static int __devexit jz4740_rtc_remove(struct platform_device *pdev)
+{
+ struct jz4740_rtc *rtc = platform_get_drvdata(pdev);
+
+ free_irq(rtc->irq, rtc);
+
+ rtc_device_unregister(rtc->rtc);
+
+ iounmap(rtc->base);
+ release_mem_region(rtc->mem->start, resource_size(rtc->mem));
+
+ kfree(rtc);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+struct platform_driver jz4740_rtc_driver = {
+ .probe = jz4740_rtc_probe,
+ .remove = __devexit_p(jz4740_rtc_remove),
+ .driver = {
+ .name = "jz4740-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_rtc_init(void)
+{
+ return platform_driver_register(&jz4740_rtc_driver);
+}
+module_init(jz4740_rtc_init);
+
+static void __exit jz4740_rtc_exit(void)
+{
+ platform_driver_unregister(&jz4740_rtc_driver);
+}
+module_exit(jz4740_rtc_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n");
+MODULE_ALIAS("platform:jz4740-rtc");
--
1.5.6.5
This patch adds support for the LCD controller on JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: [email protected]
---
drivers/video/Kconfig | 9 +
drivers/video/Makefile | 1 +
drivers/video/jz4740_fb.c | 818 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/jz4740_fb.h | 58 ++++
4 files changed, 886 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/jz4740_fb.c
create mode 100644 include/linux/jz4740_fb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 1e6fec4..f3b3893 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2229,6 +2229,15 @@ config FB_BROADSHEET
and could also have been called by other names when coupled with
a bridge adapter.
+config FB_JZ4740
+ tristate "JZ4740 LCD framebuffer support"
+ depends on FB
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ help
+ Framebuffer support for the JZ4740 SoC.
+
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ddc2af2..f56a9ca 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_FB_CARMINE) += carminefb.o
obj-$(CONFIG_FB_MB862XX) += mb862xx/
obj-$(CONFIG_FB_MSM) += msm/
obj-$(CONFIG_FB_NUC900) += nuc900fb.o
+obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/jz4740_fb.c b/drivers/video/jz4740_fb.c
new file mode 100644
index 0000000..81f280d
--- /dev/null
+++ b/drivers/video/jz4740_fb.c
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC LCD framebuffer driver
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <linux/console.h>
+#include <linux/fb.h>
+
+#include <linux/dma-mapping.h>
+
+#include <linux/jz4740_fb.h>
+#include <asm/mach-jz4740/gpio.h>
+
+#define JZ_REG_LCD_CFG 0x00
+#define JZ_REG_LCD_VSYNC 0x04
+#define JZ_REG_LCD_HSYNC 0x08
+#define JZ_REG_LCD_VAT 0x0C
+#define JZ_REG_LCD_DAH 0x10
+#define JZ_REG_LCD_DAV 0x14
+#define JZ_REG_LCD_PS 0x18
+#define JZ_REG_LCD_CLS 0x1C
+#define JZ_REG_LCD_SPL 0x20
+#define JZ_REG_LCD_REV 0x24
+#define JZ_REG_LCD_CTRL 0x30
+#define JZ_REG_LCD_STATE 0x34
+#define JZ_REG_LCD_IID 0x38
+#define JZ_REG_LCD_DA0 0x40
+#define JZ_REG_LCD_SA0 0x44
+#define JZ_REG_LCD_FID0 0x48
+#define JZ_REG_LCD_CMD0 0x4C
+#define JZ_REG_LCD_DA1 0x50
+#define JZ_REG_LCD_SA1 0x54
+#define JZ_REG_LCD_FID1 0x58
+#define JZ_REG_LCD_CMD1 0x5C
+
+#define JZ_LCD_CFG_SLCD BIT(31)
+#define JZ_LCD_CFG_PS_DISABLE BIT(23)
+#define JZ_LCD_CFG_CLS_DISABLE BIT(22)
+#define JZ_LCD_CFG_SPL_DISABLE BIT(21)
+#define JZ_LCD_CFG_REV_DISABLE BIT(20)
+#define JZ_LCD_CFG_HSYNCM BIT(19)
+#define JZ_LCD_CFG_PCLKM BIT(18)
+#define JZ_LCD_CFG_INV BIT(17)
+#define JZ_LCD_CFG_SYNC_DIR BIT(16)
+#define JZ_LCD_CFG_PS_POLARITY BIT(15)
+#define JZ_LCD_CFG_CLS_POLARITY BIT(14)
+#define JZ_LCD_CFG_SPL_POLARITY BIT(13)
+#define JZ_LCD_CFG_REV_POLARITY BIT(12)
+#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11)
+#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10)
+#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9)
+#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8)
+#define JZ_LCD_CFG_18_BIT BIT(7)
+#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4))
+#define JZ_LCD_CFG_MODE_MASK 0xf
+
+#define JZ_LCD_CTRL_BURST_4 (0x0 << 28)
+#define JZ_LCD_CTRL_BURST_8 (0x1 << 28)
+#define JZ_LCD_CTRL_BURST_16 (0x2 << 28)
+#define JZ_LCD_CTRL_RGB555 BIT(27)
+#define JZ_LCD_CTRL_OFUP BIT(26)
+#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24)
+#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24)
+#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24)
+#define JZ_LCD_CTRL_PDD_MASK (0xff << 16)
+#define JZ_LCD_CTRL_EOF_IRQ BIT(13)
+#define JZ_LCD_CTRL_SOF_IRQ BIT(12)
+#define JZ_LCD_CTRL_OFU_IRQ BIT(11)
+#define JZ_LCD_CTRL_IFU0_IRQ BIT(10)
+#define JZ_LCD_CTRL_IFU1_IRQ BIT(9)
+#define JZ_LCD_CTRL_DD_IRQ BIT(8)
+#define JZ_LCD_CTRL_QDD_IRQ BIT(7)
+#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6)
+#define JZ_LCD_CTRL_LSB_FISRT BIT(5)
+#define JZ_LCD_CTRL_DISABLE BIT(4)
+#define JZ_LCD_CTRL_ENABLE BIT(3)
+#define JZ_LCD_CTRL_BPP_1 0x0
+#define JZ_LCD_CTRL_BPP_2 0x1
+#define JZ_LCD_CTRL_BPP_4 0x2
+#define JZ_LCD_CTRL_BPP_8 0x3
+#define JZ_LCD_CTRL_BPP_15_16 0x4
+#define JZ_LCD_CTRL_BPP_18_24 0x5
+
+#define JZ_LCD_CMD_SOF_IRQ BIT(15)
+#define JZ_LCD_CMD_EOF_IRQ BIT(16)
+#define JZ_LCD_CMD_ENABLE_PAL BIT(12)
+
+#define JZ_LCD_SYNC_MASK 0x3ff
+
+#define JZ_LCD_STATE_DISABLED BIT(0)
+
+struct jzfb_framedesc {
+ uint32_t next;
+ uint32_t addr;
+ uint32_t id;
+ uint32_t cmd;
+} __attribute__((packed));
+
+struct jzfb {
+ struct fb_info *fb;
+ struct platform_device *pdev;
+ void __iomem *base;
+ struct resource *mem;
+ struct jz4740_fb_platform_data *pdata;
+
+ size_t vidmem_size;
+ void *vidmem;
+ dma_addr_t vidmem_phys;
+ struct jzfb_framedesc *framedesc;
+ dma_addr_t framedesc_phys;
+
+ struct clk *ldclk;
+ struct clk *lpclk;
+
+ unsigned is_enabled:1;
+ struct mutex lock;
+
+ uint32_t pseudo_palette[16];
+};
+
+static struct fb_fix_screeninfo jzfb_fix __devinitdata = {
+ .id = "JZ4740 FB",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .accel = FB_ACCEL_NONE,
+};
+
+static const struct jz_gpio_bulk_request jz_lcd_ctrl_pins[] = {
+ JZ_GPIO_BULK_PIN(LCD_PCLK),
+ JZ_GPIO_BULK_PIN(LCD_HSYNC),
+ JZ_GPIO_BULK_PIN(LCD_VSYNC),
+ JZ_GPIO_BULK_PIN(LCD_DE),
+ JZ_GPIO_BULK_PIN(LCD_PS),
+ JZ_GPIO_BULK_PIN(LCD_REV),
+};
+
+static const struct jz_gpio_bulk_request jz_lcd_data_pins[] = {
+ JZ_GPIO_BULK_PIN(LCD_DATA0),
+ JZ_GPIO_BULK_PIN(LCD_DATA1),
+ JZ_GPIO_BULK_PIN(LCD_DATA2),
+ JZ_GPIO_BULK_PIN(LCD_DATA3),
+ JZ_GPIO_BULK_PIN(LCD_DATA4),
+ JZ_GPIO_BULK_PIN(LCD_DATA5),
+ JZ_GPIO_BULK_PIN(LCD_DATA6),
+ JZ_GPIO_BULK_PIN(LCD_DATA7),
+ JZ_GPIO_BULK_PIN(LCD_DATA8),
+ JZ_GPIO_BULK_PIN(LCD_DATA9),
+ JZ_GPIO_BULK_PIN(LCD_DATA10),
+ JZ_GPIO_BULK_PIN(LCD_DATA11),
+ JZ_GPIO_BULK_PIN(LCD_DATA12),
+ JZ_GPIO_BULK_PIN(LCD_DATA13),
+ JZ_GPIO_BULK_PIN(LCD_DATA14),
+ JZ_GPIO_BULK_PIN(LCD_DATA15),
+ JZ_GPIO_BULK_PIN(LCD_DATA16),
+ JZ_GPIO_BULK_PIN(LCD_DATA17),
+};
+
+static unsigned int jzfb_num_ctrl_pins(struct jzfb *jzfb)
+{
+ unsigned int num;
+
+ switch (jzfb->pdata->lcd_type) {
+ case JZ_LCD_TYPE_GENERIC_16_BIT:
+ num = 4;
+ break;
+ case JZ_LCD_TYPE_GENERIC_18_BIT:
+ num = 4;
+ break;
+ case JZ_LCD_TYPE_8BIT_SERIAL:
+ num = 3;
+ break;
+ default:
+ num = 0;
+ break;
+ }
+ return num;
+}
+
+static unsigned int jzfb_num_data_pins(struct jzfb *jzfb)
+{
+ unsigned int num;
+
+ switch (jzfb->pdata->lcd_type) {
+ case JZ_LCD_TYPE_GENERIC_16_BIT:
+ num = 16;
+ break;
+ case JZ_LCD_TYPE_GENERIC_18_BIT:
+ num = 18;
+ break;
+ case JZ_LCD_TYPE_8BIT_SERIAL:
+ num = 8;
+ break;
+ default:
+ num = 0;
+ break;
+ }
+ return num;
+}
+
+/* Based on CNVT_TOHW macro from skeletonfb.c */
+static inline uint32_t jzfb_convert_color_to_hw(unsigned val,
+ struct fb_bitfield *bf)
+{
+ return (((val << bf->length) + 0x7FFF - val) >> 16) << bf->offset;
+}
+
+static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *fb)
+{
+ uint32_t color;
+
+ if (regno >= 16)
+ return -EINVAL;
+
+ color = jzfb_convert_color_to_hw(red, fb->var.red.length);
+ color |= jzfb_convert_color_to_hw(green, fb->var.green.length);
+ color |= jzfb_convert_color_to_hw(blue, fb->var.blue.length);
+ color |= jzfb_convert_color_to_hw(transp, fb->var.transp.length);
+
+ ((uint32_t *)(fb->pseudo_palette))[regno] = color;
+
+ return 0;
+}
+
+static int jzfb_get_controller_bpp(struct jzfb *jzfb)
+{
+ switch (jzfb->pdata->bpp) {
+ case 18:
+ case 24:
+ return 32;
+ case 15:
+ return 16;
+ default:
+ return jzfb->pdata->bpp;
+ }
+}
+
+static struct fb_videomode *jzfb_get_mode(struct jzfb *jzfb,
+ struct fb_var_screeninfo *var)
+{
+ size_t i;
+ struct fb_videomode *mode = jzfb->pdata->modes;
+
+ for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) {
+ if (mode->xres == var->xres && mode->yres == var->yres)
+ return mode;
+ }
+
+ return NULL;
+}
+
+static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
+{
+ struct jzfb *jzfb = fb->par;
+ struct fb_videomode *mode;
+
+ if (var->bits_per_pixel != jzfb_get_controller_bpp(jzfb) &&
+ var->bits_per_pixel != jzfb->pdata->bpp)
+ return -EINVAL;
+
+ mode = jzfb_get_mode(jzfb, var);
+ if (mode == NULL)
+ return -EINVAL;
+
+ fb_videomode_to_var(var, mode);
+
+ switch (jzfb->pdata->bpp) {
+ case 8:
+ break;
+ case 15:
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 6;
+ var->green.length = 5;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ break;
+ case 18:
+ var->red.offset = 16;
+ var->red.length = 6;
+ var->green.offset = 8;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 6;
+ var->bits_per_pixel = 32;
+ break;
+ case 32:
+ case 24:
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->bits_per_pixel = 32;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int jzfb_set_par(struct fb_info *info)
+{
+ struct jzfb *jzfb = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct fb_videomode *mode;
+ uint16_t hds, vds;
+ uint16_t hde, vde;
+ uint16_t ht, vt;
+ uint32_t ctrl;
+ uint32_t cfg;
+ unsigned long rate;
+
+ mode = jzfb_get_mode(jzfb, var);
+ if (mode == NULL)
+ return -EINVAL;
+
+ info->mode = mode;
+
+ hds = mode->hsync_len + mode->left_margin;
+ hde = hds + mode->xres;
+ ht = hde + mode->right_margin;
+
+ vds = mode->vsync_len + mode->upper_margin;
+ vde = vds + mode->yres;
+ vt = vde + mode->lower_margin;
+
+ ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16;
+
+ switch (jzfb->pdata->bpp) {
+ case 1:
+ ctrl |= JZ_LCD_CTRL_BPP_1;
+ break;
+ case 2:
+ ctrl |= JZ_LCD_CTRL_BPP_2;
+ break;
+ case 4:
+ ctrl |= JZ_LCD_CTRL_BPP_4;
+ break;
+ case 8:
+ ctrl |= JZ_LCD_CTRL_BPP_8;
+ break;
+ case 15:
+ ctrl |= JZ_LCD_CTRL_RGB555; /* Falltrough */
+ case 16:
+ ctrl |= JZ_LCD_CTRL_BPP_15_16;
+ break;
+ case 18:
+ case 24:
+ case 32:
+ ctrl |= JZ_LCD_CTRL_BPP_18_24;
+ break;
+ default:
+ break;
+ }
+
+ cfg = 0;
+ cfg |= JZ_LCD_CFG_PS_DISABLE;
+ cfg |= JZ_LCD_CFG_CLS_DISABLE;
+ cfg |= JZ_LCD_CFG_SPL_DISABLE;
+ cfg |= JZ_LCD_CFG_REV_DISABLE;
+
+ if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
+ cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
+
+ if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
+ cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
+
+ if (jzfb->pdata->pixclk_falling_edge)
+ cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;
+
+ if (jzfb->pdata->date_enable_active_low)
+ cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
+
+ if (jzfb->pdata->lcd_type == JZ_LCD_TYPE_GENERIC_18_BIT)
+ cfg |= JZ_LCD_CFG_18_BIT;
+
+ cfg |= jzfb->pdata->lcd_type & 0xf;
+
+ if (mode->pixclock) {
+ rate = PICOS2KHZ(mode->pixclock) * 1000;
+ mode->refresh = rate / vt / ht;
+ } else {
+ if (jzfb->pdata->lcd_type == JZ_LCD_TYPE_8BIT_SERIAL)
+ rate = mode->refresh * (vt + 2 * mode->xres) * ht;
+ else
+ rate = mode->refresh * vt * ht;
+
+ mode->pixclock = KHZ2PICOS(rate / 1000);
+ }
+
+ mutex_lock(&jzfb->lock);
+ if (!jzfb->is_enabled)
+ clk_enable(jzfb->ldclk);
+ else
+ ctrl |= JZ_LCD_CTRL_ENABLE;
+
+ writel(mode->hsync_len, jzfb->base + JZ_REG_LCD_HSYNC);
+ writel(mode->vsync_len, jzfb->base + JZ_REG_LCD_VSYNC);
+
+ writel((ht << 16) | vt, jzfb->base + JZ_REG_LCD_VAT);
+
+ writel((hds << 16) | hde, jzfb->base + JZ_REG_LCD_DAH);
+ writel((vds << 16) | vde, jzfb->base + JZ_REG_LCD_DAV);
+
+ writel(cfg, jzfb->base + JZ_REG_LCD_CFG);
+
+ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
+
+ if (!jzfb->is_enabled)
+ clk_disable(jzfb->ldclk);
+ mutex_unlock(&jzfb->lock);
+
+ clk_set_rate(jzfb->lpclk, rate);
+ clk_set_rate(jzfb->ldclk, rate * 3);
+
+ return 0;
+}
+
+static void jzfb_enable(struct jzfb *jzfb)
+{
+ uint32_t ctrl;
+
+ clk_enable(jzfb->ldclk);
+
+ jz_gpio_bulk_resume(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
+ jz_gpio_bulk_resume(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+
+ writel(0, jzfb->base + JZ_REG_LCD_STATE);
+
+ writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
+
+ ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
+ ctrl |= JZ_LCD_CTRL_ENABLE;
+ ctrl &= ~JZ_LCD_CTRL_DISABLE;
+ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
+}
+
+static void jzfb_disable(struct jzfb *jzfb)
+{
+ uint32_t ctrl;
+
+ ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
+ ctrl |= JZ_LCD_CTRL_DISABLE;
+ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
+ do {
+ ctrl = readl(jzfb->base + JZ_REG_LCD_STATE);
+ } while (!(ctrl & JZ_LCD_STATE_DISABLED));
+
+ jz_gpio_bulk_suspend(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
+ jz_gpio_bulk_suspend(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+
+ clk_disable(jzfb->ldclk);
+}
+
+static int jzfb_blank(int blank_mode, struct fb_info *info)
+{
+ struct jzfb *jzfb = info->par;
+
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ mutex_lock(&jzfb->lock);
+ if (jzfb->is_enabled) {
+ mutex_unlock(&jzfb->lock);
+ return 0;
+ }
+
+ jzfb_enable(jzfb);
+ jzfb->is_enabled = 1;
+
+ mutex_unlock(&jzfb->lock);
+
+ break;
+ default:
+ mutex_lock(&jzfb->lock);
+ if (!jzfb->is_enabled) {
+ mutex_unlock(&jzfb->lock);
+ return 0;
+ }
+
+ jzfb_disable(jzfb);
+
+ jzfb->is_enabled = 0;
+ mutex_unlock(&jzfb->lock);
+ break;
+ }
+
+ return 0;
+}
+
+static int jzfb_alloc_devmem(struct jzfb *jzfb)
+{
+ int max_videosize = 0;
+ struct fb_videomode *mode = jzfb->pdata->modes;
+ void *page;
+ int i;
+
+ for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) {
+ if (max_videosize < mode->xres * mode->yres)
+ max_videosize = mode->xres * mode->yres;
+ }
+
+ max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3;
+
+ jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev,
+ sizeof(*jzfb->framedesc),
+ &jzfb->framedesc_phys, GFP_KERNEL);
+
+ if (!jzfb->framedesc)
+ return -ENOMEM;
+
+ jzfb->vidmem_size = PAGE_ALIGN(max_videosize);
+ jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev,
+ jzfb->vidmem_size,
+ &jzfb->vidmem_phys, GFP_KERNEL);
+
+ if (!jzfb->vidmem)
+ goto err_free_framedesc;
+
+ for (page = jzfb->vidmem;
+ page < jzfb->vidmem + PAGE_ALIGN(jzfb->vidmem_size);
+ page += PAGE_SIZE) {
+ SetPageReserved(virt_to_page(page));
+ }
+
+ jzfb->framedesc->next = jzfb->framedesc_phys;
+ jzfb->framedesc->addr = jzfb->vidmem_phys;
+ jzfb->framedesc->id = 0xdeafbead;
+ jzfb->framedesc->cmd = 0;
+ jzfb->framedesc->cmd |= max_videosize / 4;
+
+ return 0;
+
+err_free_framedesc:
+ dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
+ jzfb->framedesc, jzfb->framedesc_phys);
+ return -ENOMEM;
+}
+
+static void jzfb_free_devmem(struct jzfb *jzfb)
+{
+ dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size,
+ jzfb->vidmem, jzfb->vidmem_phys);
+ dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
+ jzfb->framedesc, jzfb->framedesc_phys);
+}
+
+static struct fb_ops jzfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = jzfb_check_var,
+ .fb_set_par = jzfb_set_par,
+ .fb_blank = jzfb_blank,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_setcolreg = jzfb_setcolreg,
+};
+
+static int __devinit jzfb_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jzfb *jzfb;
+ struct fb_info *fb;
+ struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *mem;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Missing platform data\n");
+ return -ENOENT;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "Failed to get register memory resource\n");
+ return -ENOENT;
+ }
+
+ mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
+ if (!mem) {
+ dev_err(&pdev->dev, "Failed to request register memory region\n");
+ return -EBUSY;
+ }
+
+ fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev);
+ if (!fb) {
+ dev_err(&pdev->dev, "Failed to allocate framebuffer device\n");
+ ret = -ENOMEM;
+ goto err_release_mem_region;
+ }
+
+ fb->fbops = &jzfb_ops;
+ fb->flags = FBINFO_DEFAULT;
+
+ jzfb = fb->par;
+ jzfb->pdev = pdev;
+ jzfb->pdata = pdata;
+ jzfb->mem = mem;
+
+ jzfb->ldclk = clk_get(&pdev->dev, "lcd");
+ if (IS_ERR(jzfb->ldclk)) {
+ ret = PTR_ERR(jzfb->ldclk);
+ dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret);
+ goto err_framebuffer_release;
+ }
+
+ jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk");
+ if (IS_ERR(jzfb->lpclk)) {
+ ret = PTR_ERR(jzfb->lpclk);
+ dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret);
+ goto err_put_ldclk;
+ }
+
+ jzfb->base = ioremap(mem->start, resource_size(mem));
+
+ if (!jzfb->base) {
+ dev_err(&pdev->dev, "Failed to ioremap register memory region\n");
+ ret = -EBUSY;
+ goto err_put_lpclk;
+ }
+
+ platform_set_drvdata(pdev, jzfb);
+
+ fb_videomode_to_modelist(pdata->modes, pdata->num_modes,
+ &fb->modelist);
+ fb->mode = pdata->modes;
+
+ fb_videomode_to_var(&fb->var, fb->mode);
+ fb->var.bits_per_pixel = pdata->bpp;
+ jzfb_check_var(&fb->var, fb);
+
+ ret = jzfb_alloc_devmem(jzfb);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to allocate video memory\n");
+ goto err_iounmap;
+ }
+
+ fb->fix = jzfb_fix;
+ fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
+ fb->fix.mmio_start = mem->start;
+ fb->fix.mmio_len = resource_size(mem);
+ fb->fix.smem_start = jzfb->vidmem_phys;
+ fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
+ fb->screen_base = jzfb->vidmem;
+ fb->pseudo_palette = jzfb->pseudo_palette;
+
+ fb_alloc_cmap(&fb->cmap, 256, 0);
+
+ mutex_init(&jzfb->lock);
+
+ clk_enable(jzfb->ldclk);
+ jzfb->is_enabled = 1;
+
+ writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
+ jzfb_set_par(fb);
+
+ jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
+ jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+
+ ret = register_framebuffer(fb);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret);
+ goto err_free_devmem;
+ }
+
+ jzfb->fb = fb;
+
+ return 0;
+
+err_free_devmem:
+ jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
+ jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+
+ fb_dealloc_cmap(&fb->cmap);
+ jzfb_free_devmem(jzfb);
+err_iounmap:
+ iounmap(jzfb->base);
+err_put_lpclk:
+ clk_put(jzfb->lpclk);
+err_put_ldclk:
+ clk_put(jzfb->ldclk);
+err_framebuffer_release:
+ framebuffer_release(fb);
+err_release_mem_region:
+ release_mem_region(mem->start, resource_size(mem));
+ return ret;
+}
+
+static int __devexit jzfb_remove(struct platform_device *pdev)
+{
+ struct jzfb *jzfb = platform_get_drvdata(pdev);
+
+ jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb);
+
+ jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
+ jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+
+ iounmap(jzfb->base);
+ release_mem_region(jzfb->mem->start, resource_size(jzfb->mem));
+
+ fb_dealloc_cmap(&jzfb->fb->cmap);
+ jzfb_free_devmem(jzfb);
+
+ platform_set_drvdata(pdev, NULL);
+
+ clk_put(jzfb->lpclk);
+ clk_put(jzfb->ldclk);
+
+ framebuffer_release(jzfb->fb);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int jzfb_suspend(struct device *dev)
+{
+ struct jzfb *jzfb = dev_get_drvdata(dev);
+
+ acquire_console_sem();
+ fb_set_suspend(jzfb->fb, 1);
+ release_console_sem();
+
+ mutex_lock(&jzfb->lock);
+ if (jzfb->is_enabled)
+ jzfb_disable(jzfb);
+ mutex_unlock(&jzfb->lock);
+
+ return 0;
+}
+
+static int jzfb_resume(struct device *dev)
+{
+ struct jzfb *jzfb = dev_get_drvdata(dev);
+ clk_enable(jzfb->ldclk);
+
+ mutex_lock(&jzfb->lock);
+ if (jzfb->is_enabled)
+ jzfb_enable(jzfb);
+ mutex_unlock(&jzfb->lock);
+
+ acquire_console_sem();
+ fb_set_suspend(jzfb->fb, 0);
+ release_console_sem();
+
+ return 0;
+}
+
+static const struct dev_pm_ops jzfb_pm_ops = {
+ .suspend = jzfb_suspend,
+ .resume = jzfb_resume,
+ .poweroff = jzfb_suspend,
+ .restore = jzfb_resume,
+};
+
+#define JZFB_PM_OPS (&jzfb_pm_ops)
+
+#else
+#define JZFB_PM_OPS NULL
+#endif
+
+static struct platform_driver jzfb_driver = {
+ .probe = jzfb_probe,
+ .remove = __devexit_p(jzfb_remove),
+ .driver = {
+ .name = "jz4740-fb",
+ .pm = JZFB_PM_OPS,
+ },
+};
+
+static int __init jzfb_init(void)
+{
+ return platform_driver_register(&jzfb_driver);
+}
+module_init(jzfb_init);
+
+static void __exit jzfb_exit(void)
+{
+ platform_driver_unregister(&jzfb_driver);
+}
+module_exit(jzfb_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_DESCRIPTION("JZ4740 SoC LCD framebuffer driver");
+MODULE_ALIAS("platform:jz4740-fb");
diff --git a/include/linux/jz4740_fb.h b/include/linux/jz4740_fb.h
new file mode 100644
index 0000000..ab4c963
--- /dev/null
+++ b/include/linux/jz4740_fb.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __LINUX_JZ4740_FB_H
+#define __LINUX_JZ4740_FB_H
+
+#include <linux/fb.h>
+
+enum jz4740_fb_lcd_type {
+ JZ_LCD_TYPE_GENERIC_16_BIT = 0,
+ JZ_LCD_TYPE_GENERIC_18_BIT = 0 | (1 << 4),
+ JZ_LCD_TYPE_SPECIAL_TFT_1 = 1,
+ JZ_LCD_TYPE_SPECIAL_TFT_2 = 2,
+ JZ_LCD_TYPE_SPECIAL_TFT_3 = 3,
+ JZ_LCD_TYPE_NON_INTERLACED_CCIR656 = 5,
+ JZ_LCD_TYPE_INTERLACED_CCIR656 = 7,
+ JZ_LCD_TYPE_SINGLE_COLOR_STN = 8,
+ JZ_LCD_TYPE_SINGLE_MONOCHROME_STN = 9,
+ JZ_LCD_TYPE_DUAL_COLOR_STN = 10,
+ JZ_LCD_TYPE_DUAL_MONOCHROME_STN = 11,
+ JZ_LCD_TYPE_8BIT_SERIAL = 12,
+};
+
+/*
+* width: width of the lcd display in mm
+* height: height of the lcd display in mm
+* num_modes: size of modes
+* modes: list of valid video modes
+* bpp: bits per pixel for the lcd
+* lcd_type: lcd type
+*/
+
+struct jz4740_fb_platform_data {
+ unsigned int width;
+ unsigned int height;
+
+ size_t num_modes;
+ struct fb_videomode *modes;
+
+ unsigned int bpp;
+ enum jz4740_fb_lcd_type lcd_type;
+
+ unsigned pixclk_falling_edge:1;
+ unsigned date_enable_active_low:1;
+};
+
+#endif
--
1.5.6.5
This patch adds the Kbuild files for the JZ4740 architecture and adds JZ4740
support to the MIPS Kbuild files.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/Kconfig | 13 +++++++++++++
arch/mips/Makefile | 6 ++++++
arch/mips/jz4740/Kconfig | 8 ++++++++
arch/mips/jz4740/Makefile | 18 ++++++++++++++++++
4 files changed, 45 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/Kconfig
create mode 100644 arch/mips/jz4740/Makefile
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index cdaae94..4d44bad 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -162,6 +162,18 @@ config MACH_JAZZ
Members include the Acer PICA, MIPS Magnum 4000, MIPS Millennium and
Olivetti M700-10 workstations.
+config MACH_JZ4740
+ bool "Ingenic JZ4740 based machines"
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_LITTLE_ENDIAN
+ select DMA_NONCOHERENT
+ select IRQ_CPU
+ select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
+ select SYS_HAS_EARLY_PRINTK
+ select HAVE_PWM
+
config LASAT
bool "LASAT Networks platforms"
select CEVT_R4K
@@ -686,6 +698,7 @@ endchoice
source "arch/mips/alchemy/Kconfig"
source "arch/mips/bcm63xx/Kconfig"
source "arch/mips/jazz/Kconfig"
+source "arch/mips/jz4740/Kconfig"
source "arch/mips/lasat/Kconfig"
source "arch/mips/pmc-sierra/Kconfig"
source "arch/mips/powertv/Kconfig"
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 0b9c01a..a5cb578 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -659,6 +659,12 @@ else
load-$(CONFIG_CPU_CAVIUM_OCTEON) += 0xffffffff81100000
endif
+# Ingenic JZ4740
+#
+core-$(CONFIG_MACH_JZ4740) += arch/mips/jz4740/
+cflags-$(CONFIG_MACH_JZ4740) += -I$(srctree)/arch/mips/include/asm/mach-jz4740
+load-$(CONFIG_MACH_JZ4740) += 0xffffffff80010000
+
cflags-y += -I$(srctree)/arch/mips/include/asm/mach-generic
drivers-$(CONFIG_PCI) += arch/mips/pci/
diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
new file mode 100644
index 0000000..8a5e850
--- /dev/null
+++ b/arch/mips/jz4740/Kconfig
@@ -0,0 +1,8 @@
+choice
+ prompt "Machine type"
+ depends on MACH_JZ4740
+
+endchoice
+
+config HAVE_PWM
+ bool
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
new file mode 100644
index 0000000..a803ccb
--- /dev/null
+++ b/arch/mips/jz4740/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the Ingenic JZ4740.
+#
+
+# Object file lists.
+
+obj-y += prom.o irq.o time.o reset.o setup.o dma.o \
+ gpio.o clock.o platform.o timer.o pwm.o serial.o
+
+obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
+
+# board specific support
+
+# PM support
+
+obj-$(CONFIG_PM) += pm.o
+
+EXTRA_CFLAGS += -Werror -Wall
--
1.5.6.5
This patch adds support for serials on a JZ4740 SoC.
The JZ4740 UART interface is almost 16550A compatible.
The UART module needs to be enabled by setting a bit in the FCR register and it
has support for receive timeout interrupts.
Instead of adding yet another machine specific quirk to the 8250 serial driver
we provide a serial_out implementation which sets the required additional flags.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/jz4740/serial.c | 33 +++++++++++++++++++++++++++++++++
arch/mips/jz4740/serial.h | 21 +++++++++++++++++++++
2 files changed, 54 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/serial.c
create mode 100644 arch/mips/jz4740/serial.h
diff --git a/arch/mips/jz4740/serial.c b/arch/mips/jz4740/serial.c
new file mode 100644
index 0000000..d23de45
--- /dev/null
+++ b/arch/mips/jz4740/serial.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 serial support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+void jz4740_serial_out(struct uart_port *p, int offset, int value)
+{
+ switch (offset) {
+ case UART_FCR:
+ value |= 0x10; /* Enable uart module */
+ break;
+ case UART_IER:
+ value |= (value & 0x4) << 2;
+ break;
+ default:
+ break;
+ }
+ writeb(value, p->membase + (offset << p->regshift));
+}
diff --git a/arch/mips/jz4740/serial.h b/arch/mips/jz4740/serial.h
new file mode 100644
index 0000000..247a7d8
--- /dev/null
+++ b/arch/mips/jz4740/serial.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 serial support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __MIPS_JZ4740_SERIAL_H__
+
+void jz4740_serial_out(struct uart_port *p, int offset, int value);
+
+#endif
+
--
1.5.6.5
This patch adds support for the PWM part of the timer unit on a JZ4740 SoC.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/jz4740/pwm.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 172 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/pwm.c
diff --git a/arch/mips/jz4740/pwm.c b/arch/mips/jz4740/pwm.c
new file mode 100644
index 0000000..01aec10
--- /dev/null
+++ b/arch/mips/jz4740/pwm.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 platform PWM support
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/kernel.h>
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-jz4740/gpio.h>
+#include "timer.h"
+
+static struct clk *jz4740_pwm_clk;
+
+DEFINE_MUTEX(jz4740_pwm_mutex);
+
+struct pwm_device {
+ unsigned int id;
+ unsigned int gpio;
+ bool used;
+};
+
+static struct pwm_device jz4740_pwm_list[] = {
+ { 2, JZ_GPIO_PWM2, false },
+ { 3, JZ_GPIO_PWM3, false },
+ { 4, JZ_GPIO_PWM4, false },
+ { 5, JZ_GPIO_PWM5, false },
+ { 6, JZ_GPIO_PWM6, false },
+ { 7, JZ_GPIO_PWM7, false },
+};
+
+struct pwm_device *pwm_request(int id, const char *label)
+{
+ int ret = 0;
+ struct pwm_device *pwm;
+
+ if (!jz4740_pwm_clk) {
+ jz4740_pwm_clk = clk_get(NULL, "ext");
+
+ if (IS_ERR(jz4740_pwm_clk))
+ return ERR_PTR(PTR_ERR(jz4740_pwm_clk));
+ }
+
+ if (id < 2 || id > 7)
+ return ERR_PTR(-ENOENT);
+
+ mutex_lock(&jz4740_pwm_mutex);
+
+ pwm = &jz4740_pwm_list[id - 2];
+ if (pwm->used)
+ ret = -EBUSY;
+ else
+ pwm->used = true;
+
+ mutex_unlock(&jz4740_pwm_mutex);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = gpio_request(pwm->gpio, label);
+
+ if (ret) {
+ printk(KERN_ERR "Failed to request pwm gpio: %d\n", ret);
+ pwm->used = false;
+ return ERR_PTR(ret);
+ }
+
+ jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_PWM);
+
+ jz4740_timer_start(id);
+
+ return pwm;
+}
+
+void pwm_free(struct pwm_device *pwm)
+{
+ pwm_disable(pwm);
+ jz4740_timer_set_ctrl(pwm->id, 0);
+
+ jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_NONE);
+ gpio_free(pwm->gpio);
+
+ jz4740_timer_stop(pwm->id);
+
+ pwm->used = false;
+
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ unsigned long long tmp;
+ unsigned long period, duty;
+ unsigned int prescaler = 0;
+ unsigned int id = pwm->id;
+ uint16_t ctrl;
+ bool is_enabled;
+
+ if (duty_ns < 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ tmp = (unsigned long long)clk_get_rate(jz4740_pwm_clk) * period_ns;
+
+ do_div(tmp, 1000000000);
+
+ period = tmp;
+
+ while (period > 0xffff && prescaler < 6) {
+ period >>= 2;
+ ++prescaler;
+ }
+
+ if (prescaler == 6)
+ return -EINVAL;
+
+
+ tmp = (unsigned long long)period * duty_ns;
+ do_div(tmp, period_ns);
+ duty = period - tmp;
+
+ if (duty >= period)
+ duty = period - 1;
+
+ is_enabled = jz4740_timer_is_enabled(id);
+ if (is_enabled)
+ pwm_disable(pwm);
+
+ jz4740_timer_set_count(id, 0);
+ jz4740_timer_set_duty(id, duty);
+ jz4740_timer_set_period(id, period);
+
+ ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT |
+ JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN;
+
+ jz4740_timer_set_ctrl(id, ctrl);
+
+ if (is_enabled)
+ pwm_enable(pwm);
+
+ return 0;
+}
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id);
+ ctrl |= JZ_TIMER_CTRL_PWM_ENABLE;
+ jz4740_timer_set_ctrl(pwm->id, ctrl);
+ jz4740_timer_enable(pwm->id);
+
+ return 0;
+}
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id);
+ ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
+ jz4740_timer_disable(pwm->id);
+ jz4740_timer_set_ctrl(pwm->id, ctrl);
+}
--
1.5.6.5
This patch adds support for the NAND controller on JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: [email protected]
---
drivers/mtd/nand/Kconfig | 6 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/jz4740_nand.c | 442 +++++++++++++++++++++++++++++++++++++++
include/linux/mtd/jz4740_nand.h | 34 +++
4 files changed, 483 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/nand/jz4740_nand.c
create mode 100644 include/linux/mtd/jz4740_nand.h
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 98a04b3..5e03939 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -525,4 +525,10 @@ config MTD_NAND_NUC900
This enables the driver for the NAND Flash on evaluation board based
on w90p910 / NUC9xx.
+config MTD_NAND_JZ4740
+ tristate "Support for JZ4740 SoC NAND controller"
+ depends on MACH_JZ4740
+ help
+ Enables support for NAND Flash on JZ4740 SoC based boards.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index e8ab884..ac83dcd 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -46,5 +46,6 @@ obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
+obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
new file mode 100644
index 0000000..fee3123
--- /dev/null
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC NAND controller driver
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/mtd/jz4740_nand.h>
+#include <linux/gpio.h>
+
+#define JZ_REG_NAND_CTRL 0x50
+#define JZ_REG_NAND_ECC_CTRL 0x100
+#define JZ_REG_NAND_DATA 0x104
+#define JZ_REG_NAND_PAR0 0x108
+#define JZ_REG_NAND_PAR1 0x10C
+#define JZ_REG_NAND_PAR2 0x110
+#define JZ_REG_NAND_IRQ_STAT 0x114
+#define JZ_REG_NAND_IRQ_CTRL 0x118
+#define JZ_REG_NAND_ERR(x) (0x11C + (x << 2))
+
+#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4)
+#define JZ_NAND_ECC_CTRL_ENCODING BIT(3)
+#define JZ_NAND_ECC_CTRL_RS BIT(2)
+#define JZ_NAND_ECC_CTRL_RESET BIT(1)
+#define JZ_NAND_ECC_CTRL_ENABLE BIT(0)
+
+#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29))
+#define JZ_NAND_STATUS_PAD_FINISH BIT(4)
+#define JZ_NAND_STATUS_DEC_FINISH BIT(3)
+#define JZ_NAND_STATUS_ENC_FINISH BIT(2)
+#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1)
+#define JZ_NAND_STATUS_ERROR BIT(0)
+
+#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT(x << 1)
+#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT((x << 1) + 1)
+
+#define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000)
+#define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000)
+#define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000)
+
+struct jz_nand {
+ struct mtd_info mtd;
+ struct nand_chip chip;
+ void __iomem *base;
+ struct resource *mem;
+
+ struct jz_nand_platform_data *pdata;
+ bool is_reading;
+};
+
+static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd)
+{
+ return container_of(mtd, struct jz_nand, mtd);
+}
+
+static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct jz_nand *nand = mtd_to_jz_nand(mtd);
+ struct nand_chip *chip = mtd->priv;
+ uint32_t reg;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE));
+ if (ctrl & NAND_ALE)
+ chip->IO_ADDR_W = JZ_NAND_ADDR_ADDR;
+ else if (ctrl & NAND_CLE)
+ chip->IO_ADDR_W = JZ_NAND_CMD_ADDR;
+ else
+ chip->IO_ADDR_W = JZ_NAND_DATA_ADDR;
+
+ reg = readl(nand->base + JZ_REG_NAND_CTRL);
+ if (ctrl & NAND_NCE)
+ reg |= JZ_NAND_CTRL_ASSERT_CHIP(0);
+ else
+ reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0);
+ writel(reg, nand->base + JZ_REG_NAND_CTRL);
+ }
+ if (dat != NAND_CMD_NONE)
+ writeb(dat, chip->IO_ADDR_W);
+}
+
+static int jz_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct jz_nand *nand = mtd_to_jz_nand(mtd);
+ return gpio_get_value_cansleep(nand->pdata->busy_gpio);
+}
+
+static void jz_nand_hwctl(struct mtd_info *mtd, int mode)
+{
+ struct jz_nand *nand = mtd_to_jz_nand(mtd);
+ uint32_t reg;
+
+
+ writel(0, nand->base + JZ_REG_NAND_IRQ_STAT);
+ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
+
+ reg |= JZ_NAND_ECC_CTRL_RESET;
+ reg |= JZ_NAND_ECC_CTRL_ENABLE;
+ reg |= JZ_NAND_ECC_CTRL_RS;
+
+ switch (mode) {
+ case NAND_ECC_READ:
+ reg &= ~JZ_NAND_ECC_CTRL_ENCODING;
+ nand->is_reading = true;
+ break;
+ case NAND_ECC_WRITE:
+ reg |= JZ_NAND_ECC_CTRL_ENCODING;
+ nand->is_reading = false;
+ break;
+ default:
+ break;
+ }
+
+ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
+}
+
+
+static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ struct jz_nand *nand = mtd_to_jz_nand(mtd);
+ uint32_t reg, status;
+ int i;
+ static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4,
+ 0x8b, 0xff, 0xb7, 0x6f};
+
+ if (nand->is_reading)
+ return 0;
+
+ do {
+ status = readl(nand->base + JZ_REG_NAND_IRQ_STAT);
+ } while (!(status & JZ_NAND_STATUS_ENC_FINISH));
+
+ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
+ reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
+ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
+
+ for (i = 0; i < 9; ++i)
+ ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i);
+
+ /* If the written data is completly 0xff, we also want to write 0xff as
+ * ecc, otherwise we will get in trouble when doing subpage writes. */
+ if (memcmp(ecc_code, empty_block_ecc, 9) == 0)
+ memset(ecc_code, 0xff, 9);
+
+ return 0;
+}
+
+/*#define printkd printk*/
+#define printkd(...)
+
+static void correct_data(uint8_t *dat, int index, int mask)
+{
+ int offset = index & 0x7;
+ uint16_t data;
+ printkd("correct: ");
+
+ index += (index >> 3);
+
+ data = dat[index];
+ data |= dat[index+1] << 8;
+
+ printkd("0x%x -> ", data);
+
+ mask ^= (data >> offset) & 0x1ff;
+ data &= ~(0x1ff << offset);
+ data |= (mask << offset);
+
+ printkd("0x%x\n", data);
+
+ dat[index] = data & 0xff;
+ dat[index+1] = (data >> 8) & 0xff;
+}
+
+static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ struct jz_nand *nand = mtd_to_jz_nand(mtd);
+ int i, error_count, index;
+ uint32_t reg, status, error;
+ uint32_t t;
+
+ t = read_ecc[0];
+
+ if (t == 0xff) {
+ for (i = 1; i < 9; ++i)
+ t &= read_ecc[i];
+
+ t &= dat[0];
+ t &= dat[nand->chip.ecc.size / 2];
+ t &= dat[nand->chip.ecc.size - 1];
+
+ if (t == 0xff) {
+ for (i = 1; i < nand->chip.ecc.size - 1; ++i)
+ t &= dat[i];
+ if (t == 0xff)
+ return 0;
+ }
+ }
+
+ for (i = 0; i < 9; ++i)
+ writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i);
+
+ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
+ reg |= JZ_NAND_ECC_CTRL_PAR_READY;
+ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
+
+ do {
+ status = readl(nand->base + JZ_REG_NAND_IRQ_STAT);
+ } while (!(status & JZ_NAND_STATUS_DEC_FINISH));
+
+ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
+ reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
+ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
+
+ if (status & JZ_NAND_STATUS_ERROR) {
+ if (status & JZ_NAND_STATUS_UNCOR_ERROR) {
+ printkd("uncorrectable ecc:");
+ for (i = 0; i < 9; ++i)
+ printkd(" 0x%x", read_ecc[i]);
+ printkd("\n");
+ printkd("uncorrectable data:");
+ for (i = 0; i < 32; ++i)
+ printkd(" 0x%x", dat[i]);
+ printkd("\n");
+ return -1;
+ }
+
+ error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29;
+
+ printkd("error_count: %d %x\n", error_count, status);
+
+ for (i = 0; i < error_count; ++i) {
+ error = readl(nand->base + JZ_REG_NAND_ERR(i));
+ index = ((error >> 16) & 0x1ff) - 1;
+ if (index >= 0 && index < 512)
+ correct_data(dat, index, error & 0x1ff);
+ }
+
+ return error_count;
+ }
+
+ return 0;
+}
+
+
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+static const char *part_probes[] = {"cmdline", NULL};
+#endif
+
+static int __devinit jz_nand_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz_nand *nand;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+#ifdef CONFIG_MTD_PARTITIONS
+ struct mtd_partition *partition_info;
+ int num_partitions = 0;
+#endif
+
+ nand = kzalloc(sizeof(*nand), GFP_KERNEL);
+ if (!nand) {
+ dev_err(&pdev->dev, "Failed to allocate device structure.\n");
+ return -ENOMEM;
+ }
+
+ nand->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!nand->mem) {
+ dev_err(&pdev->dev, "Failed to get platform mmio memory\n");
+ ret = -ENOENT;
+ goto err_free;
+ }
+
+ nand->mem = request_mem_region(nand->mem->start,
+ resource_size(nand->mem), pdev->name);
+
+ if (!nand->mem) {
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ ret = -EBUSY;
+ goto err_free;
+ }
+
+ nand->base = ioremap(nand->mem->start, resource_size(nand->mem));
+
+ if (!nand->base) {
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory region\n");
+ ret = -EBUSY;
+ goto err_release_mem;
+ }
+
+ if (pdata && gpio_is_valid(pdata->busy_gpio)) {
+ ret = gpio_request(pdata->busy_gpio, "NAND busy pin");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request busy gpio %d: %d\n",
+ pdata->busy_gpio, ret);
+ goto err_iounmap;
+ }
+ }
+
+ mtd = &nand->mtd;
+ chip = &nand->chip;
+ mtd->priv = chip;
+ mtd->owner = THIS_MODULE;
+ mtd->name = "jz4740-nand";
+
+ chip->ecc.hwctl = jz_nand_hwctl;
+ chip->ecc.calculate = jz_nand_calculate_ecc_rs;
+ chip->ecc.correct = jz_nand_correct_ecc_rs;
+ chip->ecc.mode = NAND_ECC_HW_OOB_FIRST;
+ chip->ecc.size = 512;
+ chip->ecc.bytes = 9;
+ if (pdata)
+ chip->ecc.layout = pdata->ecc_layout;
+
+ chip->chip_delay = 50;
+ chip->cmd_ctrl = jz_nand_cmd_ctrl;
+
+ if (pdata && gpio_is_valid(pdata->busy_gpio))
+ chip->dev_ready = jz_nand_dev_ready;
+
+ chip->IO_ADDR_R = JZ_NAND_DATA_ADDR;
+ chip->IO_ADDR_W = JZ_NAND_DATA_ADDR;
+
+ nand->pdata = pdata;
+ platform_set_drvdata(pdev, nand);
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to scan nand\n");
+ goto err_gpio_free;
+ }
+
+ if (pdata && pdata->ident_callback) {
+ pdata->ident_callback(pdev, chip, &pdata->partitions,
+ &pdata->num_partitions);
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to scan nand\n");
+ goto err_gpio_free;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ num_partitions = parse_mtd_partitions(mtd, part_probes,
+ &partition_info, 0);
+#endif
+ if (num_partitions <= 0 && pdata) {
+ num_partitions = pdata->num_partitions;
+ partition_info = pdata->partitions;
+ }
+
+ if (num_partitions > 0)
+ ret = add_mtd_partitions(mtd, partition_info, num_partitions);
+ else
+#endif
+ ret = add_mtd_device(mtd);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add mtd device\n");
+ goto err_nand_release;
+ }
+
+ dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n");
+
+ return 0;
+err_nand_release:
+ nand_release(&nand->mtd);
+err_gpio_free:
+ platform_set_drvdata(pdev, NULL);
+ gpio_free(pdata->busy_gpio);
+err_iounmap:
+ iounmap(nand->base);
+err_release_mem:
+ release_mem_region(nand->mem->start, resource_size(nand->mem));
+err_free:
+ kfree(nand);
+ return ret;
+}
+
+static void __devexit jz_nand_remove(struct platform_device *pdev)
+{
+ struct jz_nand *nand = platform_get_drvdata(pdev);
+
+ nand_release(&nand->mtd);
+
+ iounmap(nand->base);
+
+ release_mem_region(nand->mem->start, resource_size(nand->mem));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(nand);
+}
+
+struct platform_driver jz_nand_driver = {
+ .probe = jz_nand_probe,
+ .remove = __devexit_p(jz_nand_probe),
+ .driver = {
+ .name = "jz4740-nand",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz_nand_init(void)
+{
+ return platform_driver_register(&jz_nand_driver);
+}
+module_init(jz_nand_init);
+
+static void __exit jz_nand_exit(void)
+{
+ platform_driver_unregister(&jz_nand_driver);
+}
+module_exit(jz_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC");
+MODULE_ALIAS("platform:jz4740-nand");
diff --git a/include/linux/mtd/jz4740_nand.h b/include/linux/mtd/jz4740_nand.h
new file mode 100644
index 0000000..379f9b6
--- /dev/null
+++ b/include/linux/mtd/jz4740_nand.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC NAND controller driver
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __JZ_NAND_H__
+#define __JZ_NAND_H__
+
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+struct jz_nand_platform_data {
+ int num_partitions;
+ struct mtd_partition *partitions;
+
+ struct nand_ecclayout *ecc_layout;
+
+ unsigned int busy_gpio;
+
+ void (*ident_callback)(struct platform_device *, struct nand_chip *,
+ struct mtd_partition **, int *num_partitions);
+};
+
+#endif
--
1.5.6.5
This patch adds ohci glue code for JZ4740 SoCs ohci module.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: David Brownell <[email protected]>
Cc: [email protected]
---
drivers/usb/Kconfig | 1 +
drivers/usb/host/ohci-hcd.c | 5 +
drivers/usb/host/ohci-jz4740.c | 264 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/host/ohci-jz4740.c
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 6a58cb1..39a6bfd 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -46,6 +46,7 @@ config USB_ARCH_HAS_OHCI
default y if PPC_MPC52xx
# MIPS:
default y if SOC_AU1X00
+ default y if MACH_JZ4740
# SH:
default y if CPU_SUBTYPE_SH7720
default y if CPU_SUBTYPE_SH7721
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index fc57655..05b071c 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1095,6 +1095,11 @@ MODULE_LICENSE ("GPL");
#define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver
#endif
+#ifdef CONFIG_MACH_JZ4740
+#include "ohci-jz4740.c"
+#define PLATFORM_DRIVER ohci_hcd_jz4740_driver
+#endif
+
#if !defined(PCI_DRIVER) && \
!defined(PLATFORM_DRIVER) && \
!defined(OMAP1_PLATFORM_DRIVER) && \
diff --git a/drivers/usb/host/ohci-jz4740.c b/drivers/usb/host/ohci-jz4740.c
new file mode 100644
index 0000000..0371b17
--- /dev/null
+++ b/drivers/usb/host/ohci-jz4740.c
@@ -0,0 +1,264 @@
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+struct jz4740_ohci_hcd {
+ struct ohci_hcd ohci_hcd;
+
+ struct regulator *vbus;
+ bool vbus_enabled;
+ struct clk *clk;
+};
+
+static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
+{
+ return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
+{
+ return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
+}
+
+static int ohci_jz4740_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ ret = ohci_init(ohci);
+ if (ret < 0)
+ return ret;
+
+ ohci->num_ports = 1;
+
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ dev_err(hcd->self.controller, "Can not start %s",
+ hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+ return 0;
+}
+
+static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
+ bool enabled)
+{
+ int ret = 0;
+
+ if (!jz4740_ohci->vbus)
+ return 0;
+
+ if (enabled && !jz4740_ohci->vbus_enabled) {
+ ret = regulator_enable(jz4740_ohci->vbus);
+ if (ret)
+ dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
+ "Could not power vbus\n");
+ } else if (!enabled && jz4740_ohci->vbus_enabled) {
+ ret = regulator_disable(jz4740_ohci->vbus);
+ }
+
+ if (ret == 0)
+ jz4740_ohci->vbus_enabled = enabled;
+
+ return ret;
+}
+
+static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
+ int ret;
+
+ switch (typeReq) {
+ case SetHubFeature:
+ if (wValue == USB_PORT_FEAT_POWER)
+ ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
+ break;
+ case ClearHubFeature:
+ if (wValue == USB_PORT_FEAT_POWER)
+ ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+}
+
+
+static const struct hc_driver ohci_jz4740_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "JZ4740 OHCI",
+ .hcd_priv_size = sizeof(struct jz4740_ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_jz4740_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_jz4740_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+
+static __devinit int jz4740_ohci_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct usb_hcd *hcd;
+ struct jz4740_ohci_hcd *jz4740_ohci;
+ struct resource *res;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get platform resource\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get platform irq\n");
+ return irq;
+ }
+
+ hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
+ if (!hcd) {
+ dev_err(&pdev->dev, "Failed to create hcd.\n");
+ return -ENOMEM;
+ }
+
+ jz4740_ohci = hcd_to_jz4740_hcd(hcd);
+
+ res = request_mem_region(res->start, resource_size(res), hcd_name);
+
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to request mem region.\n");
+ ret = -EBUSY;
+ goto err_free;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = ioremap(res->start, resource_size(res));
+
+ if (!hcd->regs) {
+ dev_err(&pdev->dev, "Failed to ioremap registers.\n");
+ ret = -EBUSY;
+ goto err_release_mem;
+ }
+
+ jz4740_ohci->clk = clk_get(&pdev->dev, "uhc");
+ if (IS_ERR(jz4740_ohci->clk)) {
+ ret = PTR_ERR(jz4740_ohci->clk);
+ dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
+ goto err_iounmap;
+ }
+
+ jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus");
+ if (IS_ERR(jz4740_ohci->vbus))
+ jz4740_ohci->vbus = NULL;
+
+
+ clk_set_rate(jz4740_ohci->clk, 48000000);
+ clk_enable(jz4740_ohci->clk);
+ if (jz4740_ohci->vbus)
+ ohci_jz4740_set_vbus_power(jz4740_ohci, true);
+
+ platform_set_drvdata(pdev, hcd);
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
+ goto err_disable;
+ }
+
+ return 0;
+
+err_disable:
+ platform_set_drvdata(pdev, NULL);
+ if (jz4740_ohci->vbus) {
+ regulator_disable(jz4740_ohci->vbus);
+ regulator_put(jz4740_ohci->vbus);
+ }
+ clk_disable(jz4740_ohci->clk);
+
+ clk_put(jz4740_ohci->clk);
+err_iounmap:
+ iounmap(hcd->regs);
+err_release_mem:
+ release_mem_region(res->start, resource_size(res));
+err_free:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static __devexit int jz4740_ohci_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
+
+ usb_remove_hcd(hcd);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (jz4740_ohci->vbus) {
+ regulator_disable(jz4740_ohci->vbus);
+ regulator_put(jz4740_ohci->vbus);
+ }
+
+ clk_disable(jz4740_ohci->clk);
+ clk_put(jz4740_ohci->clk);
+
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static struct platform_driver ohci_hcd_jz4740_driver = {
+ .probe = jz4740_ohci_probe,
+ .remove = __devexit_p(jz4740_ohci_remove),
+ .driver = {
+ .name = "jz4740-ohci",
+ .owner = THIS_MODULE,
+ },
+};
+
+MODULE_ALIAS("platfrom:jz4740-ohci");
--
1.5.6.5
This patch adds support for the battery voltage measurement part of the JZ4740
ADC unit.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Anton Vorontsov <[email protected]>
---
drivers/power/Kconfig | 11 +
drivers/power/Makefile | 1 +
drivers/power/jz4740-battery.c | 359 ++++++++++++++++++++++++++++++++++
include/linux/power/jz4740-battery.h | 24 +++
4 files changed, 395 insertions(+), 0 deletions(-)
create mode 100644 drivers/power/jz4740-battery.c
create mode 100644 include/linux/power/jz4740-battery.h
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 8e9ba17..80bbd0d 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -142,4 +142,15 @@ config CHARGER_PCF50633
help
Say Y to include support for NXP PCF50633 Main Battery Charger.
+config BATTERY_JZ4740
+ tristate "Ingenic JZ4740 battery"
+ depends on MACH_JZ4740
+ depends on SENSORS_JZ4740
+ help
+ Say Y to enable support for the battery on Ingenic JZ4740 based
+ boards.
+
+ This driver can be build as a module. If so, the module will be
+ called jz4740-battery.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 0005080..cf95009 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -34,3 +34,4 @@ obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
+obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c
new file mode 100644
index 0000000..9eadb36
--- /dev/null
+++ b/drivers/power/jz4740-battery.c
@@ -0,0 +1,359 @@
+/*
+ * Battery measurement code for Ingenic JZ SOC.
+ *
+ * Copyright (C) 2009 Jiejing Zhang <[email protected]>
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ *
+ * based on tosa_battery.c
+ *
+ * Copyright (C) 2008 Marek Vasut <[email protected]>
+*
+ * 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.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/power_supply.h>
+
+#include <linux/power/jz4740-battery.h>
+#include <linux/jz4740-adc.h>
+
+struct jz_battery {
+ struct jz_battery_platform_data *pdata;
+
+ int charge_irq;
+
+ int status;
+ long voltage;
+
+ struct power_supply battery;
+ struct delayed_work work;
+};
+
+static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy)
+{
+ return container_of(psy, struct jz_battery, battery);
+}
+
+static long jz_battery_read_voltage(struct jz_battery *jz_battery)
+{
+ struct device *adc = jz_battery->battery.dev->parent->parent;
+ enum jz_adc_battery_scale scale;
+
+ if (jz_battery->pdata->info.voltage_max_design > 2500000)
+ scale = JZ_ADC_BATTERY_SCALE_7V5;
+ else
+ scale = JZ_ADC_BATTERY_SCALE_2V5;
+
+ return jz4740_adc_read_battery_voltage(adc, scale);
+}
+
+static int jz_battery_get_capacity(struct power_supply *psy)
+{
+ struct jz_battery *jz_battery = psy_to_jz_battery(psy);
+ struct power_supply_info *info = &jz_battery->pdata->info;
+ long voltage;
+ int ret;
+ int voltage_span;
+
+ voltage = jz_battery_read_voltage(jz_battery);
+
+ if (voltage < 0)
+ return voltage;
+
+ voltage_span = info->voltage_max_design - info->voltage_min_design;
+ ret = ((voltage - info->voltage_min_design) * 100) / voltage_span;
+
+ if (ret > 100)
+ ret = 100;
+ else if (ret < 0)
+ ret = 0;
+
+ return ret;
+}
+
+static int jz_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct jz_battery *jz_battery = psy_to_jz_battery(psy);
+ struct power_supply_info *info = &jz_battery->pdata->info;
+ long voltage;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = jz_battery->status;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = jz_battery->pdata->info.technology;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ voltage = jz_battery_read_voltage(jz_battery);
+ if (voltage < info->voltage_min_design)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = jz_battery_get_capacity(psy);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = jz_battery_read_voltage(jz_battery);
+ if (val->intval < 0)
+ return val->intval;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = info->voltage_max_design;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = info->voltage_min_design;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void jz_battery_external_power_changed(struct power_supply *psy)
+{
+ struct jz_battery *jz_battery = psy_to_jz_battery(psy);
+
+ cancel_delayed_work(&jz_battery->work);
+ schedule_delayed_work(&jz_battery->work, 0);
+}
+
+static irqreturn_t jz_battery_charge_irq(int irq, void *data)
+{
+ struct jz_battery *jz_battery = data;
+
+ cancel_delayed_work(&jz_battery->work);
+ schedule_delayed_work(&jz_battery->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+static void jz_battery_update(struct jz_battery *jz_battery)
+{
+ int status;
+ long voltage;
+ long voltage_difference;
+ bool has_changed = 0;
+
+ if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
+ int is_charging;
+
+ is_charging = gpio_get_value(jz_battery->pdata->gpio_charge);
+ is_charging ^= jz_battery->pdata->gpio_charge_active_low;
+ if (is_charging)
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ if (status != jz_battery->status) {
+ jz_battery->status = status;
+ has_changed = 1;
+ }
+ }
+
+ voltage = jz_battery_read_voltage(jz_battery);
+ voltage_difference = voltage - jz_battery->voltage;
+ if (voltage_difference > 50000 || voltage_difference < 50000) {
+ jz_battery->voltage = voltage;
+ has_changed = 1;
+ }
+ if (has_changed)
+ power_supply_changed(&jz_battery->battery);
+}
+
+static enum power_supply_property jz_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_PRESENT,
+};
+
+static void jz_battery_work(struct work_struct *work)
+{
+ /* Too small interval will increase system workload */
+ const int interval = HZ * 30;
+ struct jz_battery *jz_battery = container_of(work, struct jz_battery,
+ work.work);
+
+ jz_battery_update(jz_battery);
+ schedule_delayed_work(&jz_battery->work, interval);
+}
+
+static int __devinit jz_battery_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct jz_battery_platform_data *pdata = pdev->dev.platform_data;
+ struct jz_battery *jz_battery;
+ struct power_supply *battery;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL);
+
+ if (!jz_battery) {
+ dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+ return -ENOMEM;
+ }
+
+ battery = &jz_battery->battery;
+ battery->name = pdata->info.name;
+ battery->type = POWER_SUPPLY_TYPE_BATTERY;
+ battery->properties = jz_battery_properties;
+ battery->num_properties = ARRAY_SIZE(jz_battery_properties);
+ battery->get_property = jz_battery_get_property;
+ battery->external_power_changed = jz_battery_external_power_changed;
+ battery->use_for_apm = 1;
+
+ jz_battery->pdata = pdata;
+
+ INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work);
+
+ if (gpio_is_valid(pdata->gpio_charge)) {
+ ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev));
+ if (ret) {
+ dev_err(&pdev->dev, "charger state gpio request failed.\n");
+ goto err_free;
+ }
+ ret = gpio_direction_input(pdata->gpio_charge);
+ if (ret) {
+ dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
+ goto err_free_gpio;
+ }
+
+ jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge);
+
+ if (jz_battery->charge_irq >= 0) {
+ ret = request_irq(jz_battery->charge_irq,
+ jz_battery_charge_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ dev_name(&pdev->dev), jz_battery);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret);
+ goto err_free_gpio;
+ }
+ }
+ } else {
+ jz_battery->charge_irq = -1;
+ }
+
+
+ ret = power_supply_register(&pdev->dev, &jz_battery->battery);
+ if (ret) {
+ dev_err(&pdev->dev, "power supply battery register failed.\n");
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, jz_battery);
+ schedule_delayed_work(&jz_battery->work, 0);
+
+ return 0;
+
+err_free_irq:
+ if (jz_battery->charge_irq >= 0)
+ free_irq(jz_battery->charge_irq, jz_battery);
+err_free_gpio:
+ if (gpio_is_valid(pdata->gpio_charge))
+ gpio_free(jz_battery->pdata->gpio_charge);
+err_free:
+ kfree(jz_battery);
+ return ret;
+}
+
+static int __devexit jz_battery_remove(struct platform_device *pdev)
+{
+ struct jz_battery *jz_battery = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&jz_battery->work);
+
+ if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
+ if (jz_battery->charge_irq >= 0)
+ free_irq(jz_battery->charge_irq, jz_battery);
+ gpio_free(jz_battery->pdata->gpio_charge);
+ }
+
+ power_supply_unregister(&jz_battery->battery);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int jz_battery_suspend(struct device *dev)
+{
+ struct jz_battery *jz_battery = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&jz_battery->work);
+ jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ return 0;
+}
+
+static int jz_battery_resume(struct device *dev)
+{
+ struct jz_battery *jz_battery = dev_get_drvdata(dev);
+
+ schedule_delayed_work(&jz_battery->work, 0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops jz_battery_pm_ops = {
+ .suspend = jz_battery_suspend,
+ .resume = jz_battery_resume,
+};
+
+#define JZ_BATTERY_PM_OPS (&jz_battery_pm_ops)
+
+#else
+#define JZ_BATTERY_PM_OPS NULL
+#endif
+
+static struct platform_driver jz_battery_driver = {
+ .probe = jz_battery_probe,
+ .remove = __devexit_p(jz_battery_remove),
+ .driver = {
+ .name = "jz4740-battery",
+ .owner = THIS_MODULE,
+ .pm = JZ_BATTERY_PM_OPS,
+ },
+};
+
+static int __init jz_battery_init(void)
+{
+ return platform_driver_register(&jz_battery_driver);
+}
+module_init(jz_battery_init);
+
+static void __exit jz_battery_exit(void)
+{
+ platform_driver_unregister(&jz_battery_driver);
+}
+module_exit(jz_battery_exit);
+
+MODULE_ALIAS("platform:jz4740-battery");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_DESCRIPTION("JZ4740 SoC battery driver");
diff --git a/include/linux/power/jz4740-battery.h b/include/linux/power/jz4740-battery.h
new file mode 100644
index 0000000..19c9610
--- /dev/null
+++ b/include/linux/power/jz4740-battery.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2009, Jiejing Zhang <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __JZ4740_BATTERY_H
+#define __JZ4740_BATTERY_H
+
+struct jz_battery_platform_data {
+ struct power_supply_info info;
+ int gpio_charge; /* GPIO port of Charger state */
+ int gpio_charge_active_low;
+};
+
+#endif
--
1.5.6.5
This patch adds support for the JZ4740 internal codec.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Liam Girdwood <[email protected]>
Cc: [email protected]
---
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/jz4740-codec.h | 20 ++
4 files changed, 528 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/codecs/jz4740-codec.c
create mode 100644 sound/soc/codecs/jz4740-codec.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 31ac553..b8008df 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4671 if I2C
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS4270 if I2C
+ select SND_SOC_JZ4740 if SOC_JZ4740
select SND_SOC_MAX9877 if I2C
select SND_SOC_DA7210 if I2C
select SND_SOC_PCM3008
@@ -269,6 +270,9 @@ config SND_SOC_WM9712
config SND_SOC_WM9713
tristate
+config SND_SOC_JZ4740_CODEC
+ tristate
+
# Amp
config SND_SOC_MAX9877
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 91429ea..4c7ee31 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
+snd-soc-jz4740-codec-objs := jz4740-codec.o
# Amp
snd-soc-max9877-objs := max9877.o
@@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
new file mode 100644
index 0000000..6e4b741
--- /dev/null
+++ b/sound/soc/codecs/jz4740-codec.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+
+#define JZ4740_REG_CODEC_1 0x0
+#define JZ4740_REG_CODEC_2 0x1
+
+#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
+#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
+#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
+#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
+#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
+#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
+#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
+#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
+#define JZ4740_CODEC_1_VREF_PULL_DOWN BIT(18)
+#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
+#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
+#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
+#define JZ4740_CODEC_1_HEADPHONE_CHANGE BIT(12)
+#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_M BIT(11)
+#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_R BIT(10)
+#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M BIT(9)
+#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN BIT(8)
+#define JZ4740_CODEC_1_SUSPEND BIT(1)
+#define JZ4740_CODEC_1_RESET BIT(0)
+
+#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
+#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
+#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
+#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
+#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
+#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
+#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET 8
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
+#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
+#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
+
+struct jz4740_codec {
+ void __iomem *base;
+ struct resource *mem;
+
+ uint32_t reg_cache[2];
+ struct snd_soc_codec codec;
+};
+
+static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
+{
+ return container_of(codec, struct jz4740_codec, codec);
+}
+
+static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
+ return readl(jz4740_codec->base + (reg << 2));
+}
+
+static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
+ jz4740_codec->reg_cache[reg] = val;
+
+ writel(val, jz4740_codec->base + (reg << 2));
+ return 0;
+}
+
+static const struct snd_kcontrol_new jz4740_codec_controls[] = {
+ SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
+ SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
+ SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
+ SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
+ SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
+ SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
+ SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
+
+ SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
+ jz4740_codec_output_controls,
+ ARRAY_SIZE(jz4740_codec_output_controls)),
+
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
+ jz4740_codec_input_controls,
+ ARRAY_SIZE(jz4740_codec_input_controls)),
+ SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_INPUT("RIN"),
+};
+
+static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
+
+ {"Line Input", NULL, "LIN"},
+ {"Line Input", NULL, "RIN"},
+
+ {"Input Mixer", "Line Capture Switch", "Line Input"},
+ {"Input Mixer", "Mic Capture Switch", "MIC"},
+
+ {"ADC", NULL, "Input Mixer"},
+
+ {"Output Mixer", "Bypass Switch", "Input Mixer"},
+ {"Output Mixer", "DAC Switch", "DAC"},
+
+ {"LOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+};
+
+static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ uint32_t val;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val = 0;
+ break;
+ case 11025:
+ val = 1;
+ break;
+ case 12000:
+ val = 2;
+ break;
+ case 16000:
+ val = 3;
+ break;
+ case 22050:
+ val = 4;
+ break;
+ case 24000:
+ val = 5;
+ break;
+ case 32000:
+ val = 6;
+ break;
+ case 44100:
+ val = 7;
+ break;
+ case 48000:
+ val = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
+
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
+
+ return 0;
+}
+
+static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
+ .hw_params = jz4740_codec_hw_params,
+ .set_fmt = jz4740_codec_set_fmt,
+};
+
+struct snd_soc_dai jz4740_codec_dai = {
+ .name = "jz-codec",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ },
+ .ops = &jz4740_codec_dai_ops,
+ .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(jz4740_codec_dai);
+
+static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+
+ if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
+ udelay(2);
+
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
+ }
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_VREF_DISABLE |
+ JZ4740_CODEC_1_VREF_AMP_DISABLE |
+ JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
+ JZ4740_CODEC_1_VREF_LOW_CURRENT |
+ JZ4740_CODEC_1_VREF_HIGH_CURRENT,
+ 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_VREF_LOW_CURRENT |
+ JZ4740_CODEC_1_VREF_HIGH_CURRENT,
+ JZ4740_CODEC_1_VREF_LOW_CURRENT |
+ JZ4740_CODEC_1_VREF_HIGH_CURRENT);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE,
+ JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SUSPEND, JZ4740_CODEC_1_SUSPEND);
+ break;
+ }
+ codec->bias_level = level;
+
+ return 0;
+}
+
+
+static struct snd_soc_codec *jz4740_codec_codec;
+
+static int jz4740_codec_dev_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = jz4740_codec_codec;
+
+ BUG_ON(!codec);
+
+ socdev->card->codec = codec;
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_add_controls(codec, jz4740_codec_controls,
+ ARRAY_SIZE(jz4740_codec_controls));
+
+ snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
+ ARRAY_SIZE(jz4740_codec_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
+ ARRAY_SIZE(jz4740_codec_dapm_routes));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+static int jz4740_codec_dev_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
+ .probe = jz4740_codec_dev_probe,
+ .remove = jz4740_codec_dev_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
+
+static int __devinit jz4740_codec_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_codec *jz4740_codec;
+ struct snd_soc_codec *codec;
+
+ jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
+
+ if (!jz4740_codec)
+ return -ENOMEM;
+
+ jz4740_codec->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!jz4740_codec->mem) {
+ dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
+ ret = -ENOENT;
+ goto err_free_codec;
+ }
+
+ jz4740_codec->mem = request_mem_region(jz4740_codec->mem->start,
+ resource_size(jz4740_codec->mem), pdev->name);
+
+ if (!jz4740_codec->mem) {
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ ret = -EBUSY;
+ goto err_free_codec;
+ }
+
+ jz4740_codec->base = ioremap(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
+
+ if (!jz4740_codec->base) {
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+ ret = -EBUSY;
+ goto err_release_mem_region;
+ }
+
+ jz4740_codec_dai.dev = &pdev->dev;
+
+ codec = &jz4740_codec->codec;
+
+ codec->dev = &pdev->dev;
+ codec->name = "jz-codec";
+ codec->owner = THIS_MODULE;
+
+ codec->read = jz4740_codec_read;
+ codec->write = jz4740_codec_write;
+ codec->set_bias_level = jz4740_codec_set_bias_level;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+
+ codec->dai = &jz4740_codec_dai;
+ codec->num_dai = 1;
+
+ codec->reg_cache = jz4740_codec->reg_cache;
+ codec->reg_cache_size = 2;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ jz4740_codec_codec = codec;
+
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
+
+
+ platform_set_drvdata(pdev, jz4740_codec);
+ ret = snd_soc_register_codec(codec);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register codec\n");
+ goto err_iounmap;
+ }
+
+ ret = snd_soc_register_dai(&jz4740_codec_dai);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register codec dai\n");
+ goto err_unregister_codec;
+ }
+
+ jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+err_unregister_codec:
+ snd_soc_unregister_codec(codec);
+err_iounmap:
+ iounmap(jz4740_codec->base);
+err_release_mem_region:
+ release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
+err_free_codec:
+ kfree(jz4740_codec);
+
+ return ret;
+}
+
+static int __devexit jz4740_codec_remove(struct platform_device *pdev)
+{
+ struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&jz4740_codec_dai);
+ snd_soc_unregister_codec(&jz4740_codec->codec);
+
+ iounmap(jz4740_codec->base);
+ release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(jz4740_codec);
+
+ return 0;
+}
+
+static struct platform_driver jz4740_codec_driver = {
+ .probe = jz4740_codec_probe,
+ .remove = __devexit_p(jz4740_codec_remove),
+ .driver = {
+ .name = "jz4740-codec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_codec_init(void)
+{
+ return platform_driver_register(&jz4740_codec_driver);
+}
+module_init(jz4740_codec_init);
+
+static void __exit jz4740_codec_exit(void)
+{
+ platform_driver_unregister(&jz4740_codec_driver);
+}
+module_exit(jz4740_codec_exit);
+
+MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:jz-codec");
diff --git a/sound/soc/codecs/jz4740-codec.h b/sound/soc/codecs/jz4740-codec.h
new file mode 100644
index 0000000..b5a0691
--- /dev/null
+++ b/sound/soc/codecs/jz4740-codec.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
+#define __SND_SOC_CODECS_JZ4740_CODEC_H__
+
+extern struct snd_soc_dai jz4740_codec_dai;
+extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
+
+#endif
--
1.5.6.5
This patch adds support for the qi_lb60 (a.k.a QI Ben NanoNote) clamshell
device.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/jz4740/Kconfig | 4 +
arch/mips/jz4740/Makefile | 2 +
arch/mips/jz4740/board-qi_lb60.c | 483 ++++++++++++++++++++++++++++++++++++++
3 files changed, 489 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/jz4740/board-qi_lb60.c
diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
index 8a5e850..3e7141f 100644
--- a/arch/mips/jz4740/Kconfig
+++ b/arch/mips/jz4740/Kconfig
@@ -1,6 +1,10 @@
choice
prompt "Machine type"
depends on MACH_JZ4740
+ default JZ4740_QI_LB60
+
+config JZ4740_QI_LB60
+ bool "Qi Hardware Ben NanoNote"
endchoice
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
index a803ccb..a604eae 100644
--- a/arch/mips/jz4740/Makefile
+++ b/arch/mips/jz4740/Makefile
@@ -11,6 +11,8 @@ obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
# board specific support
+obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
+
# PM support
obj-$(CONFIG_PM) += pm.o
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
new file mode 100644
index 0000000..cd7e065
--- /dev/null
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -0,0 +1,483 @@
+/*
+ * linux/arch/mips/jz4740/board-qi_lb60.c
+ *
+ * QI_LB60 board support
+ *
+ * Copyright (c) 2009 Qi Hardware inc.,
+ * Author: Xiangfu Liu <[email protected]>
+ * Copyright 2010, Lars-Petrer Clausen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or later
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+#include <linux/mtd/jz4740_nand.h>
+#include <linux/jz4740_fb.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mtd/jz4740_nand.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_gpio.h>
+#include <linux/power_supply.h>
+#include <linux/power/jz4740-battery.h>
+#include <linux/mmc/jz4740_mmc.h>
+
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
+
+#include <linux/leds_pwm.h>
+
+#include <asm/mach-jz4740/platform.h>
+
+#include "clock.h"
+
+static bool is_avt2;
+
+/* GPIOs */
+#define QI_LB60_GPIO_SD_CD JZ_GPIO_PORTD(0)
+#define QI_LB60_GPIO_SD_VCC_EN_N JZ_GPIO_PORTD(2)
+
+#define QI_LB60_GPIO_KEYOUT(x) (JZ_GPIO_PORTC(10) + (x))
+#define QI_LB60_GPIO_KEYIN(x) (JZ_GPIO_PORTD(18) + (x))
+#define QI_LB60_GPIO_KEYIN8 JZ_GPIO_PORTD(26)
+
+/* NAND */
+static struct nand_ecclayout qi_lb60_ecclayout_1gb = {
+ .eccbytes = 36,
+ .eccpos = {
+ 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41
+ },
+ .oobfree = {
+ { .offset = 2, .length = 4 },
+ { .offset = 42, .length = 22 }
+ },
+};
+
+/* Early prototypes of the QI LB60 had only 1GB of NAND.
+ * In order to support these devices aswell the partition and ecc layout is
+ * initalized depending on the NAND size */
+static struct mtd_partition qi_lb60_partitions_1gb[] = {
+ {
+ .name = "NAND BOOT partition",
+ .offset = 0 * 0x100000,
+ .size = 4 * 0x100000,
+ },
+ {
+ .name = "NAND KERNEL partition",
+ .offset = 4 * 0x100000,
+ .size = 4 * 0x100000,
+ },
+ {
+ .name = "NAND ROOTFS partition",
+ .offset = 8 * 0x100000,
+ .size = (504 + 512) * 0x100000,
+ },
+};
+
+static struct nand_ecclayout qi_lb60_ecclayout_2gb = {
+ .eccbytes = 72,
+ .eccpos = {
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83
+ },
+ .oobfree = {
+ { .offset = 2, .length = 10 },
+ { .offset = 84, .length = 44 },
+ },
+};
+
+static struct mtd_partition qi_lb60_partitions_2gb[] = {
+ {
+ .name = "NAND BOOT partition",
+ .offset = 0 * 0x100000,
+ .size = 4 * 0x100000,
+ },
+ {
+ .name = "NAND KERNEL partition",
+ .offset = 4 * 0x100000,
+ .size = 4 * 0x100000,
+ },
+ {
+ .name = "NAND ROOTFS partition",
+ .offset = 8 * 0x100000,
+ .size = (504 + 512 + 1024) * 0x100000,
+ },
+};
+
+static void qi_lb60_nand_ident(struct platform_device *pdev,
+ struct nand_chip *chip, struct mtd_partition **partitions,
+ int *num_partitions)
+{
+ if (chip->page_shift == 12) {
+ chip->ecc.layout = &qi_lb60_ecclayout_2gb;
+ *partitions = qi_lb60_partitions_2gb;
+ *num_partitions = ARRAY_SIZE(qi_lb60_partitions_2gb);
+ } else {
+ chip->ecc.layout = &qi_lb60_ecclayout_1gb;
+ *partitions = qi_lb60_partitions_1gb;
+ *num_partitions = ARRAY_SIZE(qi_lb60_partitions_1gb);
+ }
+}
+
+static struct jz_nand_platform_data qi_lb60_nand_pdata = {
+ .ident_callback = qi_lb60_nand_ident,
+ .busy_gpio = 94,
+};
+
+/* Keyboard*/
+
+#define KEY_QI_QI KEY_F13
+#define KEY_QI_UPRED KEY_RIGHTALT
+#define KEY_QI_VOLUP KEY_VOLUMEUP
+#define KEY_QI_VOLDOWN KEY_VOLUMEDOWN
+#define KEY_QI_FN KEY_LEFTCTRL
+
+static const uint32_t qi_lb60_keymap[] = {
+ KEY(0, 0, KEY_F1), /* S2 */
+ KEY(0, 1, KEY_F2), /* S3 */
+ KEY(0, 2, KEY_F3), /* S4 */
+ KEY(0, 3, KEY_F4), /* S5 */
+ KEY(0, 4, KEY_F5), /* S6 */
+ KEY(0, 5, KEY_F6), /* S7 */
+ KEY(0, 6, KEY_F7), /* S8 */
+
+ KEY(1, 0, KEY_Q), /* S10 */
+ KEY(1, 1, KEY_W), /* S11 */
+ KEY(1, 2, KEY_E), /* S12 */
+ KEY(1, 3, KEY_R), /* S13 */
+ KEY(1, 4, KEY_T), /* S14 */
+ KEY(1, 5, KEY_Y), /* S15 */
+ KEY(1, 6, KEY_U), /* S16 */
+ KEY(1, 7, KEY_I), /* S17 */
+ KEY(2, 0, KEY_A), /* S18 */
+ KEY(2, 1, KEY_S), /* S19 */
+ KEY(2, 2, KEY_D), /* S20 */
+ KEY(2, 3, KEY_F), /* S21 */
+ KEY(2, 4, KEY_G), /* S22 */
+ KEY(2, 5, KEY_H), /* S23 */
+ KEY(2, 6, KEY_J), /* S24 */
+ KEY(2, 7, KEY_K), /* S25 */
+ KEY(3, 0, KEY_ESC), /* S26 */
+ KEY(3, 1, KEY_Z), /* S27 */
+ KEY(3, 2, KEY_X), /* S28 */
+ KEY(3, 3, KEY_C), /* S29 */
+ KEY(3, 4, KEY_V), /* S30 */
+ KEY(3, 5, KEY_B), /* S31 */
+ KEY(3, 6, KEY_N), /* S32 */
+ KEY(3, 7, KEY_M), /* S33 */
+ KEY(4, 0, KEY_TAB), /* S34 */
+ KEY(4, 1, KEY_CAPSLOCK), /* S35 */
+ KEY(4, 2, KEY_BACKSLASH), /* S36 */
+ KEY(4, 3, KEY_APOSTROPHE), /* S37 */
+ KEY(4, 4, KEY_COMMA), /* S38 */
+ KEY(4, 5, KEY_DOT), /* S39 */
+ KEY(4, 6, KEY_SLASH), /* S40 */
+ KEY(4, 7, KEY_UP), /* S41 */
+ KEY(5, 0, KEY_O), /* S42 */
+ KEY(5, 1, KEY_L), /* S43 */
+ KEY(5, 2, KEY_EQUAL), /* S44 */
+ KEY(5, 3, KEY_QI_UPRED), /* S45 */
+ KEY(5, 4, KEY_SPACE), /* S46 */
+ KEY(5, 5, KEY_QI_QI), /* S47 */
+ KEY(5, 6, KEY_RIGHTCTRL), /* S48 */
+ KEY(5, 7, KEY_LEFT), /* S49 */
+ KEY(6, 0, KEY_F8), /* S50 */
+ KEY(6, 1, KEY_P), /* S51 */
+ KEY(6, 2, KEY_BACKSPACE),/* S52 */
+ KEY(6, 3, KEY_ENTER), /* S53 */
+ KEY(6, 4, KEY_QI_VOLUP), /* S54 */
+ KEY(6, 5, KEY_QI_VOLDOWN), /* S55 */
+ KEY(6, 6, KEY_DOWN), /* S56 */
+ KEY(6, 7, KEY_RIGHT), /* S57 */
+
+ KEY(7, 0, KEY_LEFTSHIFT), /* S58 */
+ KEY(7, 1, KEY_LEFTALT), /* S59 */
+ KEY(7, 2, KEY_QI_FN), /* S60 */
+};
+
+static const struct matrix_keymap_data qi_lb60_keymap_data = {
+ .keymap = qi_lb60_keymap,
+ .keymap_size = ARRAY_SIZE(qi_lb60_keymap),
+};
+
+static const unsigned int qi_lb60_keypad_cols[] = {
+ QI_LB60_GPIO_KEYOUT(0),
+ QI_LB60_GPIO_KEYOUT(1),
+ QI_LB60_GPIO_KEYOUT(2),
+ QI_LB60_GPIO_KEYOUT(3),
+ QI_LB60_GPIO_KEYOUT(4),
+ QI_LB60_GPIO_KEYOUT(5),
+ QI_LB60_GPIO_KEYOUT(6),
+ QI_LB60_GPIO_KEYOUT(7),
+};
+
+static const unsigned int qi_lb60_keypad_rows[] = {
+ QI_LB60_GPIO_KEYIN(0),
+ QI_LB60_GPIO_KEYIN(1),
+ QI_LB60_GPIO_KEYIN(2),
+ QI_LB60_GPIO_KEYIN(3),
+ QI_LB60_GPIO_KEYIN(4),
+ QI_LB60_GPIO_KEYIN(5),
+ QI_LB60_GPIO_KEYIN(7),
+ QI_LB60_GPIO_KEYIN8,
+};
+
+static struct matrix_keypad_platform_data qi_lb60_pdata = {
+ .keymap_data = &qi_lb60_keymap_data,
+ .col_gpios = qi_lb60_keypad_cols,
+ .row_gpios = qi_lb60_keypad_rows,
+ .num_col_gpios = ARRAY_SIZE(qi_lb60_keypad_cols),
+ .num_row_gpios = ARRAY_SIZE(qi_lb60_keypad_rows),
+ .col_scan_delay_us = 10,
+ .debounce_ms = 10,
+ .wakeup = 1,
+ .active_low = 1,
+};
+
+static struct platform_device qi_lb60_keypad = {
+ .name = "matrix-keypad",
+ .id = -1,
+ .dev = {
+ .platform_data = &qi_lb60_pdata,
+ },
+};
+
+/* Display */
+static struct fb_videomode qi_lb60_video_modes[] = {
+ {
+ .name = "320x240",
+ .xres = 320,
+ .yres = 240,
+ .refresh = 30,
+ .left_margin = 140,
+ .right_margin = 273,
+ .upper_margin = 20,
+ .lower_margin = 2,
+ .hsync_len = 1,
+ .vsync_len = 1,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+};
+
+static struct jz4740_fb_platform_data qi_lb60_fb_pdata = {
+ .width = 60,
+ .height = 45,
+ .num_modes = ARRAY_SIZE(qi_lb60_video_modes),
+ .modes = qi_lb60_video_modes,
+ .bpp = 24,
+ .lcd_type = JZ_LCD_TYPE_8BIT_SERIAL,
+ .pixclk_falling_edge = 1,
+};
+
+struct spi_gpio_platform_data spigpio_platform_data = {
+ .sck = JZ_GPIO_PORTC(23),
+ .mosi = JZ_GPIO_PORTC(22),
+ .miso = -1,
+ .num_chipselect = 1,
+};
+
+static struct platform_device spigpio_device = {
+ .name = "spi_gpio",
+ .id = 1,
+ .dev = {
+ .platform_data = &spigpio_platform_data,
+ },
+};
+
+static struct spi_board_info qi_lb60_spi_board_info[] = {
+ {
+ .modalias = "ili8960",
+ .controller_data = (void *)JZ_GPIO_PORTC(21),
+ .chip_select = 0,
+ .bus_num = 1,
+ .max_speed_hz = 30 * 1000,
+ .mode = SPI_3WIRE,
+ },
+};
+
+/* Battery */
+static struct jz_battery_platform_data qi_lb60_battery_pdata = {
+ .gpio_charge = JZ_GPIO_PORTC(27),
+ .gpio_charge_active_low = 1,
+ .info = {
+ .name = "battery",
+ .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .voltage_max_design = 4200000,
+ .voltage_min_design = 3600000,
+ },
+};
+
+/* GPIO Key: power */
+static struct gpio_keys_button qi_lb60_gpio_keys_buttons[] = {
+ [0] = {
+ .code = KEY_POWER,
+ .gpio = JZ_GPIO_PORTD(29),
+ .active_low = 1,
+ .desc = "Power",
+ .wakeup = 1,
+ },
+};
+
+static struct gpio_keys_platform_data qi_lb60_gpio_keys_data = {
+ .nbuttons = ARRAY_SIZE(qi_lb60_gpio_keys_buttons),
+ .buttons = qi_lb60_gpio_keys_buttons,
+};
+
+static struct platform_device qi_lb60_gpio_keys = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &qi_lb60_gpio_keys_data,
+ }
+};
+
+static struct jz4740_mmc_platform_data qi_lb60_mmc_pdata = {
+ .gpio_card_detect = QI_LB60_GPIO_SD_CD,
+ .gpio_read_only = -1,
+ .gpio_power = QI_LB60_GPIO_SD_VCC_EN_N,
+ .power_active_low = 1,
+};
+
+/* OHCI */
+static struct regulator_consumer_supply avt2_usb_regulator_consumer =
+ REGULATOR_SUPPLY("vbus", "jz4740-ohci");
+
+static struct regulator_init_data avt2_usb_regulator_init_data = {
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &avt2_usb_regulator_consumer,
+ .constraints = {
+ .name = "USB power",
+ .min_uV = 5000000,
+ .max_uV = 5000000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct fixed_voltage_config avt2_usb_regulator_data = {
+ .supply_name = "USB power",
+ .microvolts = 5000000,
+ .gpio = JZ_GPIO_PORTB(17),
+ .init_data = &avt2_usb_regulator_init_data,
+};
+
+static struct platform_device avt2_usb_regulator_device = {
+ .name = "reg-fixed-voltage",
+ .id = -1,
+ .dev = {
+ .platform_data = &avt2_usb_regulator_data,
+ }
+};
+
+/* pizo */
+static struct led_pwm qi_lb60_pizo_led = {
+ .name = "nanonote::pizo",
+ .pwm_id = 4,
+ .max_brightness = 255,
+ .pwm_period_ns = 1000000,
+};
+
+static struct led_pwm_platform_data qi_lb60_pizo_data = {
+ .num_leds = 1,
+ .leds = &qi_lb60_pizo_led,
+};
+
+static struct platform_device qi_lb60_pizo_device = {
+ .name = "leds_pwm",
+ .id = -1,
+ .dev = {
+ .platform_data = &qi_lb60_pizo_data,
+ }
+};
+
+static struct platform_device *jz_platform_devices[] __initdata = {
+ &jz4740_udc_device,
+ &jz4740_mmc_device,
+ &jz4740_nand_device,
+ &qi_lb60_keypad,
+ &spigpio_device,
+ &jz4740_framebuffer_device,
+ &jz4740_i2s_device,
+ &jz4740_codec_device,
+ &jz4740_rtc_device,
+ &jz4740_adc_device,
+ &jz4740_battery_device,
+ &qi_lb60_gpio_keys,
+ &qi_lb60_pizo_device,
+};
+
+static void __init board_gpio_setup(void)
+{
+ /* We only need to enable/disable pullup here for pins used in generic
+ * drivers. Everything else is done by the drivers themselfs. */
+ jz_gpio_disable_pullup(QI_LB60_GPIO_SD_VCC_EN_N);
+ jz_gpio_disable_pullup(QI_LB60_GPIO_SD_CD);
+}
+
+static int __init qi_lb60_init_platform_devices(void)
+{
+ jz4740_framebuffer_device.dev.platform_data = &qi_lb60_fb_pdata;
+ jz4740_nand_device.dev.platform_data = &qi_lb60_nand_pdata;
+ jz4740_battery_device.dev.platform_data = &qi_lb60_battery_pdata;
+ jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata;
+
+ jz4740_serial_device_register();
+
+ spi_register_board_info(qi_lb60_spi_board_info,
+ ARRAY_SIZE(qi_lb60_spi_board_info));
+
+ if (is_avt2) {
+ platform_device_register(&avt2_usb_regulator_device);
+ platform_device_register(&jz4740_usb_ohci_device);
+ }
+
+ return platform_add_devices(jz_platform_devices,
+ ARRAY_SIZE(jz_platform_devices));
+
+}
+
+struct jz4740_clock_board_data jz4740_clock_bdata = {
+ .ext_rate = 12000000,
+ .rtc_rate = 32768,
+};
+
+static __init int board_avt2(char *str)
+{
+ qi_lb60_mmc_pdata.card_detect_active_low = 1;
+ is_avt2 = true;
+
+ return 1;
+}
+__setup("avt2", board_avt2);
+
+static int __init qi_lb60_board_setup(void)
+{
+ printk(KERN_INFO "Qi Hardware JZ4740 QI %s setup\n",
+ is_avt2 ? "AVT2" : "LB60");
+
+ board_gpio_setup();
+
+ if (qi_lb60_init_platform_devices())
+ panic("Failed to initalize platform devices\n");
+
+ return 0;
+}
+arch_initcall(qi_lb60_board_setup);
--
1.5.6.5
This patch adds support for the ADC module on JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: [email protected]
---
drivers/hwmon/Kconfig | 11 ++
drivers/hwmon/Makefile | 1 +
drivers/hwmon/jz4740-adc.c | 423 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/jz4740-adc.h | 25 +++
4 files changed, 460 insertions(+), 0 deletions(-)
create mode 100644 drivers/hwmon/jz4740-adc.c
create mode 100644 include/linux/jz4740-adc.h
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e19cf8e..da79ba9 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -446,6 +446,17 @@ config SENSORS_IT87
This driver can also be built as a module. If so, the module
will be called it87.
+config SENSORS_JZ4740
+ tristate "Ingenic JZ4740 SoC ADC driver"
+ depends on MACH_JZ4740
+ help
+ If you say yes here you get support for the Ingenic JZ4740 SoC ADC core.
+ It is required for the JZ4740 battery and touchscreen driver and is used
+ to synchronize access to the adc module between those two.
+
+ This driver can also be build as a module. If so, the module will be
+ called jz4740-adc.
+
config SENSORS_LM63
tristate "National Semiconductor LM63 and LM64"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2138ceb..3e772aa 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
+obj-$(CONFIG_SENSORS_JZ4740) += jz4740-adc.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
diff --git a/drivers/hwmon/jz4740-adc.c b/drivers/hwmon/jz4740-adc.c
new file mode 100644
index 0000000..635dfe9
--- /dev/null
+++ b/drivers/hwmon/jz4740-adc.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SoC ADC driver
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This driver is meant to synchronize access to the adc core for the battery
+ * and touchscreen driver. Thus these drivers should use the adc driver as a
+ * parent.
+ */
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/clk.h>
+
+#include <linux/jz4740-adc.h>
+
+#define JZ_REG_ADC_ENABLE 0x00
+#define JZ_REG_ADC_CFG 0x04
+#define JZ_REG_ADC_CTRL 0x08
+#define JZ_REG_ADC_STATUS 0x0C
+#define JZ_REG_ADC_SAME 0x10
+#define JZ_REG_ADC_WAIT 0x14
+#define JZ_REG_ADC_TOUCH 0x18
+#define JZ_REG_ADC_BATTERY 0x1C
+#define JZ_REG_ADC_ADCIN 0x20
+
+#define JZ_ADC_ENABLE_TOUCH BIT(2)
+#define JZ_ADC_ENABLE_BATTERY BIT(1)
+#define JZ_ADC_ENABLE_ADCIN BIT(0)
+
+#define JZ_ADC_CFG_SPZZ BIT(31)
+#define JZ_ADC_CFG_EX_IN BIT(30)
+#define JZ_ADC_CFG_DNUM_MASK (0x7 << 16)
+#define JZ_ADC_CFG_DMA_ENABLE BIT(15)
+#define JZ_ADC_CFG_XYZ_MASK (0x2 << 13)
+#define JZ_ADC_CFG_SAMPLE_NUM_MASK (0x7 << 10)
+#define JZ_ADC_CFG_CLKDIV (0xf << 5)
+#define JZ_ADC_CFG_BAT_MB BIT(4)
+
+#define JZ_ADC_CFG_DNUM_OFFSET 16
+#define JZ_ADC_CFG_XYZ_OFFSET 13
+#define JZ_ADC_CFG_SAMPLE_NUM_OFFSET 10
+#define JZ_ADC_CFG_CLKDIV_OFFSET 5
+
+#define JZ_ADC_IRQ_PENDOWN BIT(4)
+#define JZ_ADC_IRQ_PENUP BIT(3)
+#define JZ_ADC_IRQ_TOUCH BIT(2)
+#define JZ_ADC_IRQ_BATTERY BIT(1)
+#define JZ_ADC_IRQ_ADCIN BIT(0)
+
+#define JZ_ADC_TOUCH_TYPE1 BIT(31)
+#define JZ_ADC_TOUCH_DATA1_MASK 0xfff
+#define JZ_ADC_TOUCH_TYPE0 BIT(15)
+#define JZ_ADC_TOUCH_DATA0_MASK 0xfff
+
+#define JZ_ADC_BATTERY_MASK 0xfff
+
+#define JZ_ADC_ADCIN_MASK 0xfff
+
+struct jz4740_adc {
+ struct resource *mem;
+ void __iomem *base;
+
+ int irq;
+
+ struct clk *clk;
+ unsigned int clk_ref;
+
+ struct device *hwmon;
+
+ struct completion bat_completion;
+ struct completion adc_completion;
+
+ spinlock_t lock;
+};
+
+static irqreturn_t jz4740_adc_irq(int irq, void *data)
+{
+ struct jz4740_adc *adc = data;
+ uint8_t status;
+
+ status = readb(adc->base + JZ_REG_ADC_STATUS);
+
+ if (status & JZ_ADC_IRQ_BATTERY)
+ complete(&adc->bat_completion);
+ if (status & JZ_ADC_IRQ_ADCIN)
+ complete(&adc->adc_completion);
+
+ writeb(0xff, adc->base + JZ_REG_ADC_STATUS);
+
+ return IRQ_HANDLED;
+}
+
+static void jz4740_adc_enable_irq(struct jz4740_adc *adc, int irq)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_CTRL);
+ val &= ~irq;
+ writeb(val, adc->base + JZ_REG_ADC_CTRL);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void jz4740_adc_disable_irq(struct jz4740_adc *adc, int irq)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_CTRL);
+ val |= irq;
+ writeb(val, adc->base + JZ_REG_ADC_CTRL);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void jz4740_adc_enable_adc(struct jz4740_adc *adc, int engine)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_ENABLE);
+ val |= engine;
+ writeb(val, adc->base + JZ_REG_ADC_ENABLE);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void jz4740_adc_disable_adc(struct jz4740_adc *adc, int engine)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_ENABLE);
+ val &= ~engine;
+ writeb(val, adc->base + JZ_REG_ADC_ENABLE);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static inline void jz4740_adc_set_cfg(struct jz4740_adc *adc, uint32_t mask,
+uint32_t val)
+{
+ unsigned long flags;
+ uint32_t cfg;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ cfg = readl(adc->base + JZ_REG_ADC_CFG);
+
+ cfg &= ~mask;
+ cfg |= val;
+
+ writel(cfg, adc->base + JZ_REG_ADC_CFG);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ if (adc->clk_ref++ == 0)
+ clk_enable(adc->clk);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ if (--adc->clk_ref == 0)
+ clk_disable(adc->clk);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+long jz4740_adc_read_battery_voltage(struct device *dev,
+ enum jz_adc_battery_scale scale)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(dev);
+ unsigned long t;
+ long long voltage;
+ uint16_t val;
+
+ if (!adc)
+ return -ENODEV;
+
+ jz4740_adc_clk_enable(adc);
+
+ if (scale == JZ_ADC_BATTERY_SCALE_2V5)
+ jz4740_adc_set_cfg(adc, JZ_ADC_CFG_BAT_MB, JZ_ADC_CFG_BAT_MB);
+ else
+ jz4740_adc_set_cfg(adc, JZ_ADC_CFG_BAT_MB, 0);
+
+ jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_BATTERY);
+ jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_BATTERY);
+
+ t = wait_for_completion_interruptible_timeout(&adc->bat_completion,
+ HZ);
+
+ jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_BATTERY);
+
+ if (t <= 0) {
+ jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_BATTERY);
+ return t ? t : -ETIMEDOUT;
+ }
+
+ val = readw(adc->base + JZ_REG_ADC_BATTERY);
+
+ jz4740_adc_clk_disable(adc);
+
+ if (scale == JZ_ADC_BATTERY_SCALE_2V5)
+ voltage = (((long long)val) * 2500000LL) >> 12LL;
+ else
+ voltage = ((((long long)val) * 7395000LL) >> 12LL) + 33000LL;
+
+ return voltage;
+}
+EXPORT_SYMBOL_GPL(jz4740_adc_read_battery_voltage);
+
+static ssize_t jz4740_adc_read_adcin(struct device *dev,
+ struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(dev);
+ unsigned long t;
+ uint16_t val;
+
+ jz4740_adc_clk_enable(adc);
+
+ jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_ADCIN);
+ jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_ADCIN);
+
+ t = wait_for_completion_interruptible_timeout(&adc->adc_completion,
+ HZ);
+
+ jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_ADCIN);
+
+ if (t <= 0) {
+ jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_ADCIN);
+ return t ? t : -ETIMEDOUT;
+ }
+
+ val = readw(adc->base + JZ_REG_ADC_ADCIN);
+ jz4740_adc_clk_disable(adc);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, jz4740_adc_read_adcin, NULL, 0);
+
+static int __devinit jz4740_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_adc *adc;
+
+ adc = kmalloc(sizeof(*adc), GFP_KERNEL);
+
+ adc->irq = platform_get_irq(pdev, 0);
+
+ if (adc->irq < 0) {
+ ret = adc->irq;
+ dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+ goto err_free;
+ }
+
+ adc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!adc->mem) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
+ goto err_free;
+ }
+
+ adc->mem = request_mem_region(adc->mem->start, resource_size(adc->mem),
+ pdev->name);
+
+ if (!adc->mem) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ goto err_free;
+ }
+
+ adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
+
+ if (!adc->base) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+ goto err_release_mem_region;
+ }
+
+ adc->clk = clk_get(&pdev->dev, "adc");
+
+ if (IS_ERR(adc->clk)) {
+ ret = PTR_ERR(adc->clk);
+ dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
+ goto err_iounmap;
+ }
+
+ init_completion(&adc->bat_completion);
+ init_completion(&adc->adc_completion);
+
+ spin_lock_init(&adc->lock);
+
+ adc->clk_ref = 0;
+
+ platform_set_drvdata(pdev, adc);
+
+ ret = request_irq(adc->irq, jz4740_adc_irq, 0, pdev->name, adc);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
+ goto err_clk_put;
+ }
+
+ ret = device_create_file(&pdev->dev, &sensor_dev_attr_in0_input.dev_attr);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create sysfs file: %d\n", ret);
+ goto err_free_irq;
+ }
+
+ adc->hwmon = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(adc->hwmon)) {
+ ret = PTR_ERR(adc->hwmon);
+ goto err_remove_file;
+ }
+
+ writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
+ writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
+
+ return 0;
+
+err_remove_file:
+ device_remove_file(&pdev->dev, &sensor_dev_attr_in0_input.dev_attr);
+err_free_irq:
+ free_irq(adc->irq, adc);
+err_clk_put:
+ clk_put(adc->clk);
+err_iounmap:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(adc->base);
+err_release_mem_region:
+ release_mem_region(adc->mem->start, resource_size(adc->mem));
+err_free:
+ kfree(adc);
+
+ return ret;
+}
+
+static int __devexit jz4740_adc_remove(struct platform_device *pdev)
+{
+ struct jz4740_adc *adc = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(adc->hwmon);
+ device_remove_file(&pdev->dev, &sensor_dev_attr_in0_input.dev_attr);
+
+ free_irq(adc->irq, adc);
+
+ iounmap(adc->base);
+ release_mem_region(adc->mem->start, resource_size(adc->mem));
+
+ clk_put(adc->clk);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(adc);
+
+ return 0;
+}
+
+struct platform_driver jz4740_adc_driver = {
+ .probe = jz4740_adc_probe,
+ .remove = __devexit_p(jz4740_adc_remove),
+ .driver = {
+ .name = "jz4740-adc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_adc_init(void)
+{
+ return platform_driver_register(&jz4740_adc_driver);
+}
+module_init(jz4740_adc_init);
+
+static void __exit jz4740_adc_exit(void)
+{
+ platform_driver_unregister(&jz4740_adc_driver);
+}
+module_exit(jz4740_adc_exit);
+
+MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jz4740-adc");
diff --git a/include/linux/jz4740-adc.h b/include/linux/jz4740-adc.h
new file mode 100644
index 0000000..59cfe63
--- /dev/null
+++ b/include/linux/jz4740-adc.h
@@ -0,0 +1,25 @@
+
+#ifndef __LINUX_JZ4740_ADC
+#define __LINUX_JZ4740_ADC
+
+#include <linux/device.h>
+
+enum jz_adc_battery_scale {
+ JZ_ADC_BATTERY_SCALE_2V5, /* Mesures voltages up to 2.5V */
+ JZ_ADC_BATTERY_SCALE_7V5, /* Mesures voltages up to 7.5V */
+};
+
+/*
+ * jz4740_adc_read_battery_voltage - Read battery voltage from the ADC PBAT pin
+ * @dev: Pointer to a jz4740-adc device
+ * @scale: Whether to use 2.5V or 7.5V scale
+ *
+ * Returns: Battery voltage in mircovolts
+ *
+ * Context: Process
+*/
+long jz4740_adc_read_battery_voltage(struct device *dev,
+ enum jz_adc_battery_scale scale);
+
+
+#endif
--
1.5.6.5
This patch adds ASoC support for JZ4740 SoCs I2S module.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Liam Girdwood <[email protected]>
Cc: [email protected]
---
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/jz4740/Kconfig | 13 +
sound/soc/jz4740/Makefile | 9 +
sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
sound/soc/jz4740/jz4740-i2s.h | 18 ++
sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
sound/soc/jz4740/jz4740-pcm.h | 22 ++
8 files changed, 982 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/jz4740/Kconfig
create mode 100644 sound/soc/jz4740/Makefile
create mode 100644 sound/soc/jz4740/jz4740-i2s.c
create mode 100644 sound/soc/jz4740/jz4740-i2s.h
create mode 100644 sound/soc/jz4740/jz4740-pcm.c
create mode 100644 sound/soc/jz4740/jz4740-pcm.h
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index b1749bc..5a7a724 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/jz4740/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 1470141..fdbe74d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += jz4740/
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
new file mode 100644
index 0000000..39df949
--- /dev/null
+++ b/sound/soc/jz4740/Kconfig
@@ -0,0 +1,13 @@
+config SND_JZ4740_SOC
+ tristate "SoC Audio for Ingenic JZ4740 SoC"
+ depends on SOC_JZ4740 && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Jz4740 AC97, I2S or SSP interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_JZ4740_SOC_I2S
+ depends on SND_JZ4740_SOC
+ tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
+ help
+ Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
new file mode 100644
index 0000000..1be8d19
--- /dev/null
+++ b/sound/soc/jz4740/Makefile
@@ -0,0 +1,9 @@
+#
+# Jz4740 Platform Support
+#
+snd-soc-jz4740-objs := jz4740-pcm.o
+snd-soc-jz4740-i2s-objs := jz4740-i2s.o
+
+obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
+obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
+
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
new file mode 100644
index 0000000..2b139fd
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "jz4740-i2s.h"
+#include "jz4740-pcm.h"
+
+#define JZ_REG_AIC_CONF 0x00
+#define JZ_REG_AIC_CTRL 0x04
+#define JZ_REG_AIC_I2S_FMT 0x10
+#define JZ_REG_AIC_FIFO_STATUS 0x14
+#define JZ_REG_AIC_I2S_STATUS 0x1c
+#define JZ_REG_AIC_CLK_DIV 0x30
+#define JZ_REG_AIC_FIFO 0x34
+
+#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
+#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
+#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
+#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
+#define JZ_AIC_CONF_I2S BIT(4)
+#define JZ_AIC_CONF_RESET BIT(3)
+#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
+#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
+#define JZ_AIC_CONF_ENABLE BIT(0)
+
+#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
+#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
+
+#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
+#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
+#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
+#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
+#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
+#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
+#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
+#define JZ_AIC_CTRL_FLUSH BIT(8)
+#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
+#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
+#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
+#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
+#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
+#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
+#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
+
+#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
+#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
+
+#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
+#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
+#define JZ_AIC_I2S_FMT_MSB BIT(0)
+
+#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
+
+#define JZ_AIC_CLK_DIV_MASK 0xf
+
+struct jz4740_i2s {
+ struct resource *mem;
+ void __iomem *base;
+ dma_addr_t phys_base;
+
+ struct clk *clk_aic;
+ struct clk *clk_i2s;
+
+ struct jz4740_pcm_config pcm_config_playback;
+ struct jz4740_pcm_config pcm_config_capture;
+};
+
+static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
+ unsigned int reg)
+{
+ return readl(i2s->base + reg);
+}
+
+static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
+ unsigned int reg, uint32_t value)
+{
+ writel(value, i2s->base + reg);
+}
+
+static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
+{
+ return dai->private_data;
+}
+
+static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf, ctrl;
+
+ if (dai->active)
+ return 0;
+
+
+ ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+ ctrl |= JZ_AIC_CTRL_FLUSH;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+ clk_enable(i2s->clk_i2s);
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf |= JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ return 0;
+}
+
+static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ if (!dai->active)
+ return;
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf &= ~JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ clk_disable(i2s->clk_i2s);
+}
+
+
+static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ uint32_t ctrl;
+ uint32_t mask;
+
+ if (playback)
+ mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
+ else
+ mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
+
+ ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ctrl |= mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ctrl &= ~mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+ return 0;
+}
+
+
+static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+
+ uint32_t format = 0;
+ uint32_t conf;
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+
+ conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
+ format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_MSB:
+ format |= JZ_AIC_I2S_FMT_MSB;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+ jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
+
+ return 0;
+}
+
+static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ enum jz4740_dma_width dma_width;
+ struct jz4740_pcm_config *pcm_config;
+ unsigned int sample_size;
+ uint32_t ctrl;
+
+ ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ sample_size = 0;
+ dma_width = JZ4740_DMA_WIDTH_8BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S16:
+ sample_size = 1;
+ dma_width = JZ4740_DMA_WIDTH_16BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (playback) {
+ ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
+ ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
+ } else {
+ ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
+ ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
+ }
+
+ switch (params_channels(params)) {
+ case 2:
+ break;
+ case 1:
+ if (playback) {
+ ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
+ break;
+ }
+ default: /* Falltrough */
+ return -EINVAL;
+ }
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+ if (playback) {
+ pcm_config = &i2s->pcm_config_playback;
+ pcm_config->dma_config.dst_width = dma_width;
+ } else {
+ pcm_config = &i2s->pcm_config_capture;
+ pcm_config->dma_config.src_width = dma_width;
+ }
+
+
+ snd_soc_dai_set_dma_data(dai, substream, pcm_config);
+
+ return 0;
+}
+
+static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+
+ switch (div_id) {
+ case JZ4740_I2S_BIT_CLK:
+ if (div & 1 || div > 16)
+ return -EINVAL;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ int ret = 0;
+ struct clk *parent;
+
+ switch (clk_id) {
+ case JZ4740_I2S_CLKSRC_EXT:
+ parent = clk_get(NULL, "ext");
+ clk_set_parent(i2s->clk_i2s, parent);
+ break;
+ case JZ4740_I2S_CLKSRC_PLL:
+ parent = clk_get(NULL, "pll half");
+ clk_set_parent(i2s->clk_i2s, parent);
+ ret = clk_set_rate(i2s->clk_i2s, freq);
+ break;
+ default:
+ return -EINVAL;
+ }
+ clk_put(parent);
+
+ return ret;
+}
+
+static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ if (dai->active) {
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf &= ~JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ clk_disable(i2s->clk_i2s);
+ }
+
+ clk_disable(i2s->clk_aic);
+
+ return 0;
+}
+
+static int jz4740_i2s_resume(struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ clk_enable(i2s->clk_aic);
+
+ if (dai->active) {
+ clk_enable(i2s->clk_i2s);
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf |= JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ }
+
+ return 0;
+}
+
+static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
+ (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
+ JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
+ JZ_AIC_CONF_I2S |
+ JZ_AIC_CONF_INTERNAL_CODEC;
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ return 0;
+}
+
+
+static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
+ .startup = jz4740_i2s_startup,
+ .shutdown = jz4740_i2s_shutdown,
+ .trigger = jz4740_i2s_trigger,
+ .hw_params = jz4740_i2s_hw_params,
+ .set_fmt = jz4740_i2s_set_fmt,
+ .set_clkdiv = jz4740_i2s_set_clkdiv,
+ .set_sysclk = jz4740_i2s_set_sysclk,
+};
+
+#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+
+struct snd_soc_dai jz4740_i2s_dai = {
+ .name = "jz4740-i2s",
+ .probe = jz4740_i2s_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = JZ4740_I2S_FMTS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = JZ4740_I2S_FMTS,
+ },
+ .symmetric_rates = 1,
+ .ops = &jz4740_i2s_dai_ops,
+ .suspend = jz4740_i2s_suspend,
+ .resume = jz4740_i2s_resume,
+};
+EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
+
+static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
+{
+ struct jz4740_dma_config *dma_config;
+
+ /* Playback */
+ dma_config = &i2s->pcm_config_playback.dma_config;
+ dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
+ dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
+ dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
+ dma_config->mode = JZ4740_DMA_MODE_SINGLE;
+ i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+
+ /* Capture */
+ dma_config = &i2s->pcm_config_capture.dma_config;
+ dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
+ dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
+ dma_config->flags = JZ4740_DMA_DST_AUTOINC;
+ dma_config->mode = JZ4740_DMA_MODE_SINGLE;
+ i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+}
+
+static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
+{
+ struct jz4740_i2s *i2s;
+ int ret;
+
+ i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
+
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!i2s->mem) {
+ ret = -ENOENT;
+ goto err_free;
+ }
+
+ i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
+ pdev->name);
+
+ if (!i2s->mem) {
+ ret = -EBUSY;
+ goto err_free;
+ }
+
+ i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
+
+ if (!i2s->base) {
+ ret = -EBUSY;
+ goto err_release_mem_region;
+ }
+
+ i2s->phys_base = i2s->mem->start;
+
+ i2s->clk_aic = clk_get(&pdev->dev, "aic");
+ if (IS_ERR(i2s->clk_aic)) {
+ ret = PTR_ERR(i2s->clk_aic);
+ goto err_iounmap;
+ }
+
+ i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
+ if (IS_ERR(i2s->clk_i2s)) {
+ ret = PTR_ERR(i2s->clk_i2s);
+ goto err_iounmap;
+ }
+
+ clk_enable(i2s->clk_aic);
+
+ jz4740_i2c_init_pcm_config(i2s);
+
+ jz4740_i2s_dai.private_data = i2s;
+ ret = snd_soc_register_dai(&jz4740_i2s_dai);
+
+ platform_set_drvdata(pdev, i2s);
+
+ return 0;
+
+err_iounmap:
+ iounmap(i2s->base);
+err_release_mem_region:
+ release_mem_region(i2s->mem->start, resource_size(i2s->mem));
+err_free:
+ kfree(i2s);
+
+ return ret;
+}
+
+static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
+{
+ struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&jz4740_i2s_dai);
+
+ clk_disable(i2s->clk_aic);
+ clk_put(i2s->clk_i2s);
+ clk_put(i2s->clk_aic);
+
+ iounmap(i2s->base);
+ release_mem_region(i2s->mem->start, resource_size(i2s->mem));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(i2s);
+
+ return 0;
+}
+
+static struct platform_driver jz4740_i2s_driver = {
+ .probe = jz4740_i2s_dev_probe,
+ .remove = __devexit_p(jz4740_i2s_dev_remove),
+ .driver = {
+ .name = "jz4740-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_i2s_init(void)
+{
+ return platform_driver_register(&jz4740_i2s_driver);
+}
+module_init(jz4740_i2s_init);
+
+static void __exit jz4740_i2s_exit(void)
+{
+ platform_driver_unregister(&jz4740_i2s_driver);
+}
+module_exit(jz4740_i2s_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen, <[email protected]>");
+MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jz4740-i2s");
diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
new file mode 100644
index 0000000..da22ed8
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-i2s.h
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+#ifndef _JZ4740_I2S_H
+#define _JZ4740_I2S_H
+
+/* I2S clock source */
+#define JZ4740_I2S_CLKSRC_EXT 0
+#define JZ4740_I2S_CLKSRC_PLL 1
+
+#define JZ4740_I2S_BIT_CLK 0
+
+extern struct snd_soc_dai jz4740_i2s_dai;
+
+#endif
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
new file mode 100644
index 0000000..fd1c203
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-pcm.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/mach-jz4740/dma.h>
+#include "jz4740-pcm.h"
+
+struct jz4740_runtime_data {
+ unsigned int dma_period;
+ dma_addr_t dma_start;
+ dma_addr_t dma_pos;
+ dma_addr_t dma_end;
+
+ struct jz4740_dma_chan *dma;
+
+ dma_addr_t fifo_addr;
+};
+
+/* identify hardware playback capabilities */
+static const struct snd_pcm_hardware jz4740_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .period_bytes_min = 16,
+ .period_bytes_max = 2 * PAGE_SIZE,
+ .periods_min = 2,
+ .periods_max = 128,
+ .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
+ .fifo_size = 32,
+};
+
+static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
+{
+ unsigned int count;
+
+ if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
+ count = prtd->dma_end - prtd->dma_pos;
+ else
+ count = prtd->dma_period;
+
+ jz4740_dma_disable(prtd->dma);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
+ jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
+ } else {
+ jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
+ jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
+ }
+
+ jz4740_dma_set_transfer_count(prtd->dma, count);
+
+ jz4740_dma_enable(prtd->dma);
+
+ prtd->dma_pos += prtd->dma_period;
+ if (prtd->dma_pos >= prtd->dma_end)
+ prtd->dma_pos = prtd->dma_start;
+}
+
+static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
+ void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+
+ snd_pcm_period_elapsed(substream);
+
+ jz4740_pcm_start_transfer(prtd, substream->stream);
+}
+
+static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct jz4740_pcm_config *config;
+
+ config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
+ if (!prtd->dma) {
+ const char *dma_channel_name;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_channel_name = "PCM Playback";
+ else
+ dma_channel_name = "PCM Capture";
+
+ prtd->dma = jz4740_dma_request(substream, dma_channel_name);
+ }
+
+ if (!prtd->dma)
+ return -EBUSY;
+
+ jz4740_dma_configure(prtd->dma, &config->dma_config);
+ prtd->fifo_addr = config->fifo_addr;
+
+ jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->dma_period = params_period_bytes(params);
+ prtd->dma_start = runtime->dma_addr;
+ prtd->dma_pos = prtd->dma_start;
+ prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
+
+ return 0;
+}
+
+static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct jz4740_runtime_data *prtd = substream->runtime->private_data;
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ if (prtd->dma) {
+ jz4740_dma_free(prtd->dma);
+ prtd->dma = NULL;
+ }
+
+ return 0;
+}
+
+static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct jz4740_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ if (!prtd->dma)
+ return 0;
+
+ prtd->dma_pos = prtd->dma_start;
+
+ return ret;
+}
+
+static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ jz4740_pcm_start_transfer(prtd, substream->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ jz4740_dma_disable(prtd->dma);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+ unsigned long count, pos;
+ snd_pcm_uframes_t offset;
+ struct jz4740_dma_chan *dma = prtd->dma;
+
+ count = jz4740_dma_get_residue(dma);
+ if (prtd->dma_pos == prtd->dma_start)
+ pos = prtd->dma_end - prtd->dma_start - count;
+ else
+ pos = prtd->dma_pos - prtd->dma_start - count;
+
+ offset = bytes_to_frames(runtime, pos);
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+static int jz4740_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd;
+
+ snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
+ prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
+
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+ return 0;
+}
+
+static int jz4740_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+
+ return 0;
+}
+
+static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static struct snd_pcm_ops jz4740_pcm_ops = {
+ .open = jz4740_pcm_open,
+ .close = jz4740_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = jz4740_pcm_hw_params,
+ .hw_free = jz4740_pcm_hw_free,
+ .prepare = jz4740_pcm_prepare,
+ .trigger = jz4740_pcm_trigger,
+ .pointer = jz4740_pcm_pointer,
+ .mmap = jz4740_pcm_mmap,
+};
+
+static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = jz4740_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+
+ buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+
+ return 0;
+}
+
+static void jz4740_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_noncoherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
+
+int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &jz4740_pcm_dmamask;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (dai->playback.channels_min) {
+ ret = jz4740_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto err;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = jz4740_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto err;
+ }
+
+err:
+ return ret;
+}
+
+struct snd_soc_platform jz4740_soc_platform = {
+ .name = "jz4740-pcm",
+ .pcm_ops = &jz4740_pcm_ops,
+ .pcm_new = jz4740_pcm_new,
+ .pcm_free = jz4740_pcm_free,
+};
+EXPORT_SYMBOL_GPL(jz4740_soc_platform);
+
+static int __init jz4740_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&jz4740_soc_platform);
+}
+module_init(jz4740_soc_platform_init);
+
+static void __exit jz4740_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&jz4740_soc_platform);
+}
+module_exit(jz4740_soc_platform_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
new file mode 100644
index 0000000..e3f221e
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-pcm.h
@@ -0,0 +1,22 @@
+/*
+ *
+ * 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.
+ */
+
+#ifndef _JZ4740_PCM_H
+#define _JZ4740_PCM_H
+
+#include <linux/dma-mapping.h>
+#include <asm/mach-jz4740/dma.h>
+
+/* platform data */
+extern struct snd_soc_platform jz4740_soc_platform;
+
+struct jz4740_pcm_config {
+ struct jz4740_dma_config dma_config;
+ phys_addr_t fifo_addr;
+};
+
+#endif
--
1.5.6.5
This patch adds support for the mmc controller on JZ4740 SoCs.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: [email protected]
---
drivers/mmc/host/Kconfig | 8 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/jz4740_mmc.c | 1000 ++++++++++++++++++++++++++++++++++++++++
include/linux/mmc/jz4740_mmc.h | 15 +
4 files changed, 1024 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/host/jz4740_mmc.c
create mode 100644 include/linux/mmc/jz4740_mmc.h
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index e171e77..6c3162b 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -81,6 +81,14 @@ config MMC_RICOH_MMC
If unsure, say Y.
+config MMC_JZ4740
+ tristate "JZ4740 SD/Multimedia Card Interface support"
+ depends on MACH_JZ4740
+ help
+ This selects the Ingenic Z4740 SD/Multimedia card Interface.
+ If you have an ngenic platform with a Multimedia Card slot,
+ say Y or M here.
+
config MMC_SDHCI_OF
tristate "SDHCI support on OpenFirmware platforms"
depends on MMC_SDHCI && PPC_OF
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e30c2ee..f4e53c9 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
+obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
sdhci-of-y := sdhci-of-core.o
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
new file mode 100644
index 0000000..1034b85
--- /dev/null
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
+ * JZ4740 SD/MMC controller driver
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/scatterlist.h>
+#include <linux/clk.h>
+#include <linux/mmc/jz4740_mmc.h>
+
+#include <linux/gpio.h>
+#include <asm/mach-jz4740/gpio.h>
+#include <asm/cacheflush.h>
+#include <linux/dma-mapping.h>
+
+#define JZ_REG_MMC_STRPCL 0x00
+#define JZ_REG_MMC_STATUS 0x04
+#define JZ_REG_MMC_CLKRT 0x08
+#define JZ_REG_MMC_CMDAT 0x0C
+#define JZ_REG_MMC_RESTO 0x10
+#define JZ_REG_MMC_RDTO 0x14
+#define JZ_REG_MMC_BLKLEN 0x18
+#define JZ_REG_MMC_NOB 0x1C
+#define JZ_REG_MMC_SNOB 0x20
+#define JZ_REG_MMC_IMASK 0x24
+#define JZ_REG_MMC_IREG 0x28
+#define JZ_REG_MMC_CMD 0x2C
+#define JZ_REG_MMC_ARG 0x30
+#define JZ_REG_MMC_RESP_FIFO 0x34
+#define JZ_REG_MMC_RXFIFO 0x38
+#define JZ_REG_MMC_TXFIFO 0x3C
+
+#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7)
+#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6)
+#define JZ_MMC_STRPCL_START_READWAIT BIT(5)
+#define JZ_MMC_STRPCL_STOP_READWAIT BIT(4)
+#define JZ_MMC_STRPCL_RESET BIT(3)
+#define JZ_MMC_STRPCL_START_OP BIT(2)
+#define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0))
+#define JZ_MMC_STRPCL_CLOCK_STOP BIT(0)
+#define JZ_MMC_STRPCL_CLOCK_START BIT(1)
+
+
+#define JZ_MMC_STATUS_IS_RESETTING BIT(15)
+#define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14)
+#define JZ_MMC_STATUS_PRG_DONE BIT(13)
+#define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12)
+#define JZ_MMC_STATUS_END_CMD_RES BIT(11)
+#define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10)
+#define JZ_MMC_STATUS_IS_READWAIT BIT(9)
+#define JZ_MMC_STATUS_CLK_EN BIT(8)
+#define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7)
+#define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6)
+#define JZ_MMC_STATUS_CRC_RES_ERR BIT(5)
+#define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4)
+#define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3)
+#define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2)
+#define JZ_MMC_STATUS_TIMEOUT_RES BIT(1)
+#define JZ_MMC_STATUS_TIMEOUT_READ BIT(0)
+
+#define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0))
+#define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2))
+
+
+#define JZ_MMC_CMDAT_IO_ABORT BIT(11)
+#define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10)
+#define JZ_MMC_CMDAT_DMA_EN BIT(8)
+#define JZ_MMC_CMDAT_INIT BIT(7)
+#define JZ_MMC_CMDAT_BUSY BIT(6)
+#define JZ_MMC_CMDAT_STREAM BIT(5)
+#define JZ_MMC_CMDAT_WRITE BIT(4)
+#define JZ_MMC_CMDAT_DATA_EN BIT(3)
+#define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0))
+#define JZ_MMC_CMDAT_RSP_R1 1
+#define JZ_MMC_CMDAT_RSP_R2 2
+#define JZ_MMC_CMDAT_RSP_R3 3
+
+#define JZ_MMC_IRQ_SDIO BIT(7)
+#define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6)
+#define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5)
+#define JZ_MMC_IRQ_END_CMD_RES BIT(2)
+#define JZ_MMC_IRQ_PRG_DONE BIT(1)
+#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0)
+
+
+#define JZ_MMC_CLK_RATE 24000000
+
+#define JZ4740_MMC_MAX_TIMEOUT 10000000
+
+struct jz4740_mmc_host {
+ struct mmc_host *mmc;
+ struct platform_device *pdev;
+ struct jz4740_mmc_platform_data *pdata;
+ struct clk *clk;
+
+ int irq;
+ int card_detect_irq;
+
+ struct resource *mem;
+ void __iomem *base;
+ struct mmc_request *req;
+ struct mmc_command *cmd;
+
+ int max_clock;
+ uint32_t cmdat;
+
+ uint16_t irq_mask;
+
+ spinlock_t lock;
+ struct timer_list clock_timer;
+ struct timer_list timeout_timer;
+ unsigned waiting:1;
+};
+
+static void jz4740_mmc_cmd_done(struct jz4740_mmc_host *host);
+
+static void jz4740_mmc_enable_irq(struct jz4740_mmc_host *host, unsigned int irq)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->irq_mask &= ~irq;
+ writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void jz4740_mmc_disable_irq(struct jz4740_mmc_host *host, unsigned int irq)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->irq_mask |= irq;
+ writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host,
+ bool start_transfer)
+{
+ uint16_t val = JZ_MMC_STRPCL_CLOCK_START;
+
+ if (start_transfer)
+ val |= JZ_MMC_STRPCL_START_OP;
+
+ writew(val, host->base + JZ_REG_MMC_STRPCL);
+}
+
+static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host)
+{
+ uint32_t status;
+
+ writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL);
+ do {
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+ } while (status & JZ_MMC_STATUS_CLK_EN);
+
+}
+
+static void jz4740_mmc_reset(struct jz4740_mmc_host *host)
+{
+ uint32_t status;
+
+ writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL);
+ udelay(10);
+ do {
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+ } while (status & JZ_MMC_STATUS_IS_RESETTING);
+}
+
+static void jz4740_mmc_request_done(struct jz4740_mmc_host *host)
+{
+ struct mmc_request *req;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ req = host->req;
+ host->req = NULL;
+ host->waiting = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (!unlikely(req))
+ return;
+
+ mmc_request_done(host->mmc, req);
+}
+
+static inline unsigned int jz4740_mmc_wait_irq(struct jz4740_mmc_host *host,
+ unsigned int irq)
+{
+ unsigned int timeout = JZ4740_MMC_MAX_TIMEOUT;
+ uint16_t status;
+
+ do {
+ status = readw(host->base + JZ_REG_MMC_IREG);
+ } while (!(status & irq) && --timeout);
+
+ return timeout;
+}
+
+static void jz4740_mmc_write_data(struct jz4740_mmc_host *host,
+ struct mmc_data *data)
+{
+ struct scatterlist *sg;
+ uint32_t *sg_pointer;
+ int status;
+ unsigned int timeout;
+ size_t i, j;
+
+ for (sg = data->sg; sg; sg = sg_next(sg)) {
+ sg_pointer = sg_virt(sg);
+ i = sg->length / 4;
+ j = i >> 3;
+ i = i & 0x7;
+ while (j) {
+ timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ);
+ if (unlikely(timeout == 0))
+ goto err_timeout;
+
+ writel(sg_pointer[0], host->base + JZ_REG_MMC_TXFIFO);
+ writel(sg_pointer[1], host->base + JZ_REG_MMC_TXFIFO);
+ writel(sg_pointer[2], host->base + JZ_REG_MMC_TXFIFO);
+ writel(sg_pointer[3], host->base + JZ_REG_MMC_TXFIFO);
+ writel(sg_pointer[4], host->base + JZ_REG_MMC_TXFIFO);
+ writel(sg_pointer[5], host->base + JZ_REG_MMC_TXFIFO);
+ writel(sg_pointer[6], host->base + JZ_REG_MMC_TXFIFO);
+ writel(sg_pointer[7], host->base + JZ_REG_MMC_TXFIFO);
+ sg_pointer += 8;
+ --j;
+ }
+ if (i) {
+ timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ);
+ if (unlikely(timeout == 0))
+ goto err_timeout;
+
+ while (i) {
+ writel(*sg_pointer, host->base + JZ_REG_MMC_TXFIFO);
+ ++sg_pointer;
+ --i;
+ }
+ }
+ data->bytes_xfered += sg->length;
+ }
+
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+ if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK)
+ goto err;
+
+ timeout = JZ4740_MMC_MAX_TIMEOUT;
+ do {
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+ } while ((status & JZ_MMC_STATUS_DATA_TRAN_DONE) == 0 && --timeout);
+
+ if (unlikely(timeout == 0))
+ goto err_timeout;
+ writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG);
+
+ return;
+err_timeout:
+ host->req->cmd->error = -ETIMEDOUT;
+ data->error = -ETIMEDOUT;
+ return;
+err:
+ if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) {
+ host->req->cmd->error = -ETIMEDOUT;
+ data->error = -ETIMEDOUT;
+ } else {
+ host->req->cmd->error = -EIO;
+ data->error = -EIO;
+ }
+}
+
+static void jz4740_mmc_timeout(unsigned long data)
+{
+ struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (!host->waiting) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ host->waiting = 0;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ host->req->cmd->error = -ETIMEDOUT;
+ jz4740_mmc_request_done(host);
+}
+
+static void jz4740_mmc_read_data(struct jz4740_mmc_host *host,
+ struct mmc_data *data)
+{
+ struct scatterlist *sg;
+ uint32_t *sg_pointer;
+ uint32_t d;
+ uint16_t status = 0;
+ size_t i, j;
+ unsigned int timeout;
+
+ for (sg = data->sg; sg; sg = sg_next(sg)) {
+ sg_pointer = sg_virt(sg);
+ i = sg->length;
+ j = i >> 5;
+ i = i & 0x1f;
+ while (j) {
+ timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ);
+ if (unlikely(timeout == 0))
+ goto err_timeout;
+
+ sg_pointer[0] = readl(host->base + JZ_REG_MMC_RXFIFO);
+ sg_pointer[1] = readl(host->base + JZ_REG_MMC_RXFIFO);
+ sg_pointer[2] = readl(host->base + JZ_REG_MMC_RXFIFO);
+ sg_pointer[3] = readl(host->base + JZ_REG_MMC_RXFIFO);
+ sg_pointer[4] = readl(host->base + JZ_REG_MMC_RXFIFO);
+ sg_pointer[5] = readl(host->base + JZ_REG_MMC_RXFIFO);
+ sg_pointer[6] = readl(host->base + JZ_REG_MMC_RXFIFO);
+ sg_pointer[7] = readl(host->base + JZ_REG_MMC_RXFIFO);
+
+ sg_pointer += 8;
+ --j;
+ }
+
+ while (i >= 4) {
+ timeout = jz4740_mmc_wait_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ);
+ if (unlikely(timeout == 0))
+ goto err_timeout;
+
+ *sg_pointer = readl(host->base + JZ_REG_MMC_RXFIFO);
+ ++sg_pointer;
+ i -= 4;
+ }
+ if (i > 0) {
+ d = readl(host->base + JZ_REG_MMC_RXFIFO);
+ memcpy(sg_pointer, &d, i);
+ }
+ data->bytes_xfered += sg->length;
+
+ flush_dcache_page(sg_page(sg));
+ }
+
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+ if (status & JZ_MMC_STATUS_READ_ERROR_MASK)
+ goto err;
+
+ /* For whatever reason there is sometime one word more in the fifo then
+ * requested */
+ while ((status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) == 0 && --timeout) {
+ d = readl(host->base + JZ_REG_MMC_RXFIFO);
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+ }
+ return;
+err_timeout:
+ host->req->cmd->error = -ETIMEDOUT;
+ data->error = -ETIMEDOUT;
+ return;
+err:
+ if (status & JZ_MMC_STATUS_TIMEOUT_READ) {
+ host->req->cmd->error = -ETIMEDOUT;
+ data->error = -ETIMEDOUT;
+ } else {
+ host->req->cmd->error = -EIO;
+ data->error = -EIO;
+ }
+}
+
+static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
+{
+ struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid;
+
+ if (host->cmd->error)
+ jz4740_mmc_request_done(host);
+ else
+ jz4740_mmc_cmd_done(host);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t jz_mmc_irq(int irq, void *devid)
+{
+ struct jz4740_mmc_host *host = devid;
+ uint16_t irq_reg, status, tmp;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ irq_reg = readw(host->base + JZ_REG_MMC_IREG);
+
+ tmp = irq_reg;
+ spin_lock_irqsave(&host->lock, flags);
+ irq_reg &= ~host->irq_mask;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ |
+ JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE);
+
+ if (tmp != irq_reg) {
+ dev_warn(&host->pdev->dev, "Sparse irq: %x\n", tmp & ~irq_reg);
+ writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG);
+ }
+
+
+ if (irq_reg & JZ_MMC_IRQ_SDIO) {
+ writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG);
+ mmc_signal_sdio_irq(host->mmc);
+ }
+
+ if (!host->req || !host->cmd)
+ goto handled;
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (!host->waiting) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ goto handled;
+ }
+
+ host->waiting = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ del_timer(&host->timeout_timer);
+
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+
+ if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
+ host->cmd->error = -ETIMEDOUT;
+ } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) {
+ host->cmd->error = -EIO;
+ } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR |
+ JZ_MMC_STATUS_CRC_WRITE_ERROR)) {
+ host->cmd->data->error = -EIO;
+ } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR |
+ JZ_MMC_STATUS_CRC_WRITE_ERROR)) {
+ host->cmd->data->error = -EIO;
+ }
+
+ if (irq_reg & JZ_MMC_IRQ_END_CMD_RES) {
+ jz4740_mmc_disable_irq(host, JZ_MMC_IRQ_END_CMD_RES);
+ writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+handled:
+
+ writew(0xff, host->base + JZ_REG_MMC_IREG);
+ return IRQ_HANDLED;
+}
+
+static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate)
+{
+ int div = 0;
+ int real_rate;
+
+ jz4740_mmc_clock_disable(host);
+ clk_set_rate(host->clk, JZ_MMC_CLK_RATE);
+
+ real_rate = clk_get_rate(host->clk);
+
+ while (real_rate > rate && div < 7) {
+ ++div;
+ real_rate >>= 1;
+ }
+
+ writew(div, host->base + JZ_REG_MMC_CLKRT);
+ return real_rate;
+}
+
+
+static void jz4740_mmc_read_response(struct jz4740_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ int i;
+ uint16_t tmp;
+ if (cmd->flags & MMC_RSP_136) {
+ tmp = readw(host->base + JZ_REG_MMC_RESP_FIFO);
+ for (i = 0; i < 4; ++i) {
+ cmd->resp[i] = tmp << 24;
+ cmd->resp[i] |= readw(host->base + JZ_REG_MMC_RESP_FIFO) << 8;
+ tmp = readw(host->base + JZ_REG_MMC_RESP_FIFO);
+ cmd->resp[i] |= tmp >> 8;
+ }
+ } else {
+ cmd->resp[0] = readw(host->base + JZ_REG_MMC_RESP_FIFO) << 24;
+ cmd->resp[0] |= readw(host->base + JZ_REG_MMC_RESP_FIFO) << 8;
+ cmd->resp[0] |= readw(host->base + JZ_REG_MMC_RESP_FIFO) & 0xff;
+ }
+}
+
+static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ uint32_t cmdat = host->cmdat;
+
+ host->cmdat &= ~JZ_MMC_CMDAT_INIT;
+ jz4740_mmc_clock_disable(host);
+
+ host->cmd = cmd;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdat |= JZ_MMC_CMDAT_BUSY;
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_R1B:
+ case MMC_RSP_R1:
+ cmdat |= JZ_MMC_CMDAT_RSP_R1;
+ break;
+ case MMC_RSP_R2:
+ cmdat |= JZ_MMC_CMDAT_RSP_R2;
+ break;
+ case MMC_RSP_R3:
+ cmdat |= JZ_MMC_CMDAT_RSP_R3;
+ break;
+ default:
+ break;
+ }
+
+ if (cmd->data) {
+ cmdat |= JZ_MMC_CMDAT_DATA_EN;
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ cmdat |= JZ_MMC_CMDAT_WRITE;
+ if (cmd->data->flags & MMC_DATA_STREAM)
+ cmdat |= JZ_MMC_CMDAT_STREAM;
+
+ writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
+ writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
+ }
+
+ writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD);
+ writel(cmd->arg, host->base + JZ_REG_MMC_ARG);
+ writel(cmdat, host->base + JZ_REG_MMC_CMDAT);
+
+ host->waiting = 1;
+ jz4740_mmc_clock_enable(host, 1);
+ mod_timer(&host->timeout_timer, jiffies + 5*HZ);
+}
+
+static void jz4740_mmc_cmd_done(struct jz4740_mmc_host *host)
+{
+ uint32_t status;
+ struct mmc_command *cmd = host->req->cmd;
+ struct mmc_request *req = host->req;
+ unsigned int timeout = JZ4740_MMC_MAX_TIMEOUT;
+
+ if (cmd->flags & MMC_RSP_PRESENT)
+ jz4740_mmc_read_response(host, cmd);
+
+ if (cmd->data) {
+ if (cmd->data->flags & MMC_DATA_READ)
+ jz4740_mmc_read_data(host, cmd->data);
+ else
+ jz4740_mmc_write_data(host, cmd->data);
+ }
+
+ if (req->stop) {
+ jz4740_mmc_send_command(host, req->stop);
+ do {
+ status = readw(host->base + JZ_REG_MMC_IREG);
+ } while ((status & JZ_MMC_IRQ_PRG_DONE) == 0 && --timeout);
+ writew(JZ_MMC_IRQ_PRG_DONE, host->base + JZ_REG_MMC_IREG);
+ }
+
+ if (unlikely(timeout == 0))
+ req->stop->error = -ETIMEDOUT;
+
+ jz4740_mmc_request_done(host);
+}
+
+static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct jz4740_mmc_host *host = mmc_priv(mmc);
+
+ host->req = req;
+
+ writew(0xffff, host->base + JZ_REG_MMC_IREG);
+
+ writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG);
+ jz4740_mmc_enable_irq(host, JZ_MMC_IRQ_END_CMD_RES);
+ jz4740_mmc_send_command(host, req->cmd);
+}
+
+
+static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct jz4740_mmc_host *host = mmc_priv(mmc);
+ if (ios->clock)
+ jz4740_mmc_set_clock_rate(host, ios->clock);
+
+ switch (ios->power_mode) {
+ case MMC_POWER_UP:
+ jz4740_mmc_reset(host);
+ if (gpio_is_valid(host->pdata->gpio_power))
+ gpio_set_value(host->pdata->gpio_power,
+ !host->pdata->power_active_low);
+ host->cmdat |= JZ_MMC_CMDAT_INIT;
+ clk_enable(host->clk);
+ break;
+ case MMC_POWER_ON:
+ break;
+ default:
+ if (gpio_is_valid(host->pdata->gpio_power))
+ gpio_set_value(host->pdata->gpio_power,
+ host->pdata->power_active_low);
+ clk_disable(host->clk);
+ break;
+ }
+
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_4BIT;
+ break;
+ case MMC_BUS_WIDTH_4:
+ host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT;
+ break;
+ default:
+ break;
+ }
+}
+
+static int jz4740_mmc_get_ro(struct mmc_host *mmc)
+{
+ struct jz4740_mmc_host *host = mmc_priv(mmc);
+ if (!gpio_is_valid(host->pdata->gpio_read_only))
+ return -ENOSYS;
+
+ return gpio_get_value(host->pdata->gpio_read_only) ^
+ host->pdata->read_only_active_low;
+}
+
+static int jz4740_mmc_get_cd(struct mmc_host *mmc)
+{
+ struct jz4740_mmc_host *host = mmc_priv(mmc);
+ if (!gpio_is_valid(host->pdata->gpio_card_detect))
+ return -ENOSYS;
+
+ return gpio_get_value(host->pdata->gpio_card_detect) ^
+ host->pdata->card_detect_active_low;
+}
+
+static irqreturn_t jz4740_mmc_card_detect_irq(int irq, void *devid)
+{
+ struct jz4740_mmc_host *host = devid;
+
+ mmc_detect_change(host->mmc, HZ / 3);
+
+ return IRQ_HANDLED;
+}
+
+static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct jz4740_mmc_host *host = mmc_priv(mmc);
+ if (enable)
+ jz4740_mmc_enable_irq(host, JZ_MMC_IRQ_SDIO);
+ else
+ jz4740_mmc_disable_irq(host, JZ_MMC_IRQ_SDIO);
+}
+
+static const struct mmc_host_ops jz4740_mmc_ops = {
+ .request = jz4740_mmc_request,
+ .set_ios = jz4740_mmc_set_ios,
+ .get_ro = jz4740_mmc_get_ro,
+ .get_cd = jz4740_mmc_get_cd,
+ .enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
+};
+
+static const struct jz_gpio_bulk_request jz4740_mmc_pins[] = {
+ JZ_GPIO_BULK_PIN(MSC_CMD),
+ JZ_GPIO_BULK_PIN(MSC_CLK),
+ JZ_GPIO_BULK_PIN(MSC_DATA0),
+ JZ_GPIO_BULK_PIN(MSC_DATA1),
+ JZ_GPIO_BULK_PIN(MSC_DATA2),
+ JZ_GPIO_BULK_PIN(MSC_DATA3),
+};
+
+static int __devinit jz4740_mmc_request_gpios(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return 0;
+
+ if (gpio_is_valid(pdata->gpio_card_detect)) {
+ ret = gpio_request(pdata->gpio_card_detect, "MMC detect change");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request detect change gpio\n");
+ goto err;
+ }
+ gpio_direction_input(pdata->gpio_card_detect);
+ }
+
+ if (gpio_is_valid(pdata->gpio_read_only)) {
+ ret = gpio_request(pdata->gpio_read_only, "MMC read only");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request read only gpio: %d\n", ret);
+ goto err_free_gpio_card_detect;
+ }
+ gpio_direction_input(pdata->gpio_read_only);
+ }
+
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request(pdata->gpio_power, "MMC power");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request power gpio: %d\n", ret);
+ goto err_free_gpio_read_only;
+ }
+ gpio_direction_output(pdata->gpio_power, pdata->power_active_low);
+ }
+
+ return 0;
+
+err_free_gpio_read_only:
+ if (gpio_is_valid(pdata->gpio_read_only))
+ gpio_free(pdata->gpio_read_only);
+err_free_gpio_card_detect:
+ if (gpio_is_valid(pdata->gpio_card_detect))
+ gpio_free(pdata->gpio_card_detect);
+err:
+ return ret;
+}
+
+static void jz4740_mmc_free_gpios(struct platform_device *pdev)
+{
+ struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return;
+
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+ if (gpio_is_valid(pdata->gpio_read_only))
+ gpio_free(pdata->gpio_read_only);
+ if (gpio_is_valid(pdata->gpio_card_detect))
+ gpio_free(pdata->gpio_card_detect);
+}
+
+static inline size_t jz4740_mmc_num_pins(struct jz4740_mmc_host *host)
+{
+ size_t num_pins = ARRAY_SIZE(jz4740_mmc_pins);
+ if (host->pdata && host->pdata->data_1bit)
+ num_pins -= 3;
+
+ return num_pins;
+}
+
+static int __devinit jz4740_mmc_probe(struct platform_device* pdev)
+{
+ int ret;
+ struct mmc_host *mmc;
+ struct jz4740_mmc_host *host;
+ struct jz4740_mmc_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+
+ mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
+
+ if (!mmc) {
+ dev_err(&pdev->dev, "Failed to alloc mmc host structure\n");
+ return -ENOMEM;
+ }
+
+ host = mmc_priv(mmc);
+ host->pdata = pdata;
+
+ host->irq = platform_get_irq(pdev, 0);
+
+ if (host->irq < 0) {
+ ret = host->irq;
+ dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+ goto err_free_host;
+ }
+
+ host->clk = clk_get(&pdev->dev, "mmc");
+ if (!host->clk) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get mmc clock\n");
+ goto err_free_host;
+ }
+
+ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!host->mem) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get base platform memory\n");
+ goto err_clk_put;
+ }
+
+ host->mem = request_mem_region(host->mem->start,
+ resource_size(host->mem), pdev->name);
+
+ if (!host->mem) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to request base memory region\n");
+ goto err_clk_put;
+ }
+
+ host->base = ioremap_nocache(host->mem->start, resource_size(host->mem));
+
+ if (!host->base) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to ioremap base memory\n");
+ goto err_release_mem_region;
+ }
+
+ ret = jz_gpio_bulk_request(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request mmc pins: %d\n", ret);
+ goto err_iounmap;
+ }
+
+ ret = jz4740_mmc_request_gpios(pdev);
+ if (ret)
+ goto err_gpio_bulk_free;
+
+ mmc->ops = &jz4740_mmc_ops;
+ mmc->f_min = JZ_MMC_CLK_RATE / 128;
+ mmc->f_max = JZ_MMC_CLK_RATE;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA;
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
+
+ mmc->max_blk_size = (1 << 10) - 1;
+ mmc->max_blk_count = (1 << 15) - 1;
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+
+ mmc->max_phys_segs = 128;
+ mmc->max_hw_segs = 128;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ host->mmc = mmc;
+ host->pdev = pdev;
+ host->max_clock = JZ_MMC_CLK_RATE;
+ spin_lock_init(&host->lock);
+ host->irq_mask = 0xffff;
+
+ host->card_detect_irq = gpio_to_irq(pdata->gpio_card_detect);
+
+ if (host->card_detect_irq < 0) {
+ dev_warn(&pdev->dev, "Failed to get irq for card detect gpio\n");
+ } else {
+ ret = request_irq(host->card_detect_irq,
+ jz4740_mmc_card_detect_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "MMC card detect", host);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request card detect irq");
+ goto err_free_gpios;
+ }
+ }
+
+ ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker,
+ IRQF_DISABLED, dev_name(&pdev->dev), host);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
+ goto err_free_card_detect_irq;
+ }
+
+ jz4740_mmc_reset(host);
+ jz4740_mmc_clock_disable(host);
+ setup_timer(&host->timeout_timer, jz4740_mmc_timeout,
+ (unsigned long)host);
+
+ platform_set_drvdata(pdev, host);
+ ret = mmc_add_host(mmc);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret);
+ goto err_free_irq;
+ }
+ dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
+
+ return 0;
+
+err_free_irq:
+ free_irq(host->irq, host);
+err_free_card_detect_irq:
+ if (host->card_detect_irq >= 0)
+ free_irq(host->card_detect_irq, host);
+err_free_gpios:
+ jz4740_mmc_free_gpios(pdev);
+err_gpio_bulk_free:
+ jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
+err_iounmap:
+ iounmap(host->base);
+err_release_mem_region:
+ release_mem_region(host->mem->start, resource_size(host->mem));
+err_clk_put:
+ clk_put(host->clk);
+err_free_host:
+ platform_set_drvdata(pdev, NULL);
+ mmc_free_host(mmc);
+
+ return ret;
+}
+
+static int __devexit jz4740_mmc_remove(struct platform_device *pdev)
+{
+ struct jz4740_mmc_host *host = platform_get_drvdata(pdev);
+
+ del_timer_sync(&host->timeout_timer);
+ jz4740_mmc_disable_irq(host, 0xff);
+ jz4740_mmc_reset(host);
+
+ mmc_remove_host(host->mmc);
+
+ free_irq(host->irq, host);
+ if (host->card_detect_irq >= 0)
+ free_irq(host->card_detect_irq, host);
+
+ jz4740_mmc_free_gpios(pdev);
+ jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
+
+ iounmap(host->base);
+ release_mem_region(host->mem->start, resource_size(host->mem));
+
+ clk_put(host->clk);
+
+ platform_set_drvdata(pdev, NULL);
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int jz4740_mmc_suspend(struct device *dev)
+{
+ struct jz4740_mmc_host *host = dev_get_drvdata(dev);
+
+ mmc_suspend_host(host->mmc);
+
+ jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
+
+ return 0;
+}
+
+static int jz4740_mmc_resume(struct device *dev)
+{
+ struct jz4740_mmc_host *host = dev_get_drvdata(dev);
+
+ jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
+
+ mmc_resume_host(host->mmc);
+
+ return 0;
+}
+
+const struct dev_pm_ops jz4740_mmc_pm_ops = {
+ .suspend = jz4740_mmc_suspend,
+ .resume = jz4740_mmc_resume,
+ .poweroff = jz4740_mmc_suspend,
+ .restore = jz4740_mmc_resume,
+};
+
+#define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops)
+#else
+#define JZ4740_MMC_PM_OPS NULL
+#endif
+
+static struct platform_driver jz4740_mmc_driver = {
+ .probe = jz4740_mmc_probe,
+ .remove = __devexit_p(jz4740_mmc_remove),
+ .driver = {
+ .name = "jz4740-mmc",
+ .owner = THIS_MODULE,
+ .pm = JZ4740_MMC_PM_OPS,
+ },
+};
+
+static int __init jz4740_mmc_init(void)
+{
+ return platform_driver_register(&jz4740_mmc_driver);
+}
+module_init(jz4740_mmc_init);
+
+static void __exit jz4740_mmc_exit(void)
+{
+ platform_driver_unregister(&jz4740_mmc_driver);
+}
+module_exit(jz4740_mmc_exit);
+
+MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
diff --git a/include/linux/mmc/jz4740_mmc.h b/include/linux/mmc/jz4740_mmc.h
new file mode 100644
index 0000000..8543f43
--- /dev/null
+++ b/include/linux/mmc/jz4740_mmc.h
@@ -0,0 +1,15 @@
+#ifndef __LINUX_MMC_JZ4740_MMC
+#define __LINUX_MMC_JZ4740_MMC
+
+struct jz4740_mmc_platform_data {
+ int gpio_power;
+ int gpio_card_detect;
+ int gpio_read_only;
+ unsigned card_detect_active_low:1;
+ unsigned read_only_active_low:1;
+ unsigned power_active_low:1;
+
+ unsigned data_1bit:1;
+};
+
+#endif
--
1.5.6.5
This patch adds ASoC support for the qi_lb60 board.
Signed-off-by: Lars-Peter Clausen <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Liam Girdwood <[email protected]>
Cc: [email protected]
---
sound/soc/jz4740/Kconfig | 8 ++
sound/soc/jz4740/Makefile | 4 +
sound/soc/jz4740/qi_lb60.c | 175 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 187 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/jz4740/qi_lb60.c
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index 39df949..aaa116f 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -11,3 +11,11 @@ config SND_JZ4740_SOC_I2S
tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
+
+config SND_JZ4740_SOC_QI_LB60
+ tristate "SoC Audio support for Qi Hardware Ben Nanonote"
+ depends on SND_JZ4740_SOC && JZ4740_QI_LB60
+ select SND_JZ4740_SOC_I2S
+ select SND_SOC_JZ4740_CODEC
+ help
+ Say Y if you want to add support for SoC audio of internal codec on Ingenic Jz4740 QI_LB60 board.
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
index 1be8d19..be873c1 100644
--- a/sound/soc/jz4740/Makefile
+++ b/sound/soc/jz4740/Makefile
@@ -7,3 +7,7 @@ snd-soc-jz4740-i2s-objs := jz4740-i2s.o
obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
+# Jz4740 Machine Support
+snd-soc-qi-lb60-objs := qi_lb60.o
+
+obj-$(CONFIG_SND_JZ4740_SOC_QI_LB60) += snd-soc-qi-lb60.o
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
new file mode 100644
index 0000000..c3f1f91
--- /dev/null
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <[email protected]>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+
+#include "../codecs/jz4740-codec.h"
+#include "jz4740-pcm.h"
+#include "jz4740-i2s.h"
+
+
+#define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29)
+#define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4)
+
+static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *ctrl, int event)
+{
+ int on = 0;
+ if (event & SND_SOC_DAPM_POST_PMU)
+ on = 1;
+ else if (event & SND_SOC_DAPM_PRE_PMD)
+ on = 0;
+
+ gpio_set_value(QI_LB60_SND_GPIO, on);
+ gpio_set_value(QI_LB60_AMP_GPIO, on);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget qi_lb60_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", qi_lb60_spk_event),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route qi_lb60_routes[] = {
+ {"Mic", NULL, "MIC"},
+ {"Speaker", NULL, "LOUT"},
+ {"Speaker", NULL, "ROUT"},
+};
+
+#define QI_LB60_DAIFMT (SND_SOC_DAIFMT_I2S | \
+ SND_SOC_DAIFMT_NB_NF | \
+ SND_SOC_DAIFMT_CBM_CFM)
+
+static int qi_lb60_codec_init(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct snd_soc_dai *cpu_dai = codec->socdev->card->dai_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = codec->socdev->card->dai_link->codec_dai;
+
+ snd_soc_dapm_nc_pin(codec, "LIN");
+ snd_soc_dapm_nc_pin(codec, "RIN");
+
+ ret = snd_soc_dai_set_fmt(codec_dai, QI_LB60_DAIFMT);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set codec dai format: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(codec, qi_lb60_widgets, ARRAY_SIZE(qi_lb60_widgets));
+
+ snd_soc_dapm_add_routes(codec, qi_lb60_routes, ARRAY_SIZE(qi_lb60_routes));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link qi_lb60_dai = {
+ .name = "jz4740-codec",
+ .stream_name = "jz4740-codec",
+ .cpu_dai = &jz4740_i2s_dai,
+ .codec_dai = &jz4740_codec_dai,
+ .init = qi_lb60_codec_init,
+};
+
+static struct snd_soc_card qi_lb60 = {
+ .name = "QI LB60",
+ .dai_link = &qi_lb60_dai,
+ .num_links = 1,
+ .platform = &jz4740_soc_platform,
+};
+
+static struct snd_soc_device qi_lb60_snd_devdata = {
+ .card = &qi_lb60,
+ .codec_dev = &soc_codec_dev_jz4740_codec,
+};
+
+static struct platform_device *qi_lb60_snd_device;
+
+static int __init qi_lb60_init(void)
+{
+ int ret;
+
+ qi_lb60_snd_device = platform_device_alloc("soc-audio", -1);
+
+ if (!qi_lb60_snd_device)
+ return -ENOMEM;
+
+
+ ret = gpio_request(QI_LB60_SND_GPIO, "SND");
+ if (ret) {
+ pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
+ QI_LB60_SND_GPIO, ret);
+ goto err_device_put;
+ }
+
+ ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
+ if (ret) {
+ pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
+ QI_LB60_AMP_GPIO, ret);
+ goto err_gpio_free_snd;
+ }
+
+ gpio_direction_output(JZ_GPIO_PORTB(29), 0);
+ gpio_direction_output(JZ_GPIO_PORTD(4), 0);
+
+ platform_set_drvdata(qi_lb60_snd_device, &qi_lb60_snd_devdata);
+ qi_lb60_snd_devdata.dev = &qi_lb60_snd_device->dev;
+ ret = platform_device_add(qi_lb60_snd_device);
+ if (ret) {
+ pr_err("qi_lb60 snd: Failed to add snd soc device: %d\n", ret);
+ goto err_unset_pdata;
+ }
+
+ return 0;
+
+err_unset_pdata:
+ platform_set_drvdata(qi_lb60_snd_device, NULL);
+/*err_gpio_free_amp:*/
+ gpio_free(QI_LB60_AMP_GPIO);
+err_gpio_free_snd:
+ gpio_free(QI_LB60_SND_GPIO);
+err_device_put:
+ platform_device_put(qi_lb60_snd_device);
+
+ return ret;
+}
+module_init(qi_lb60_init);
+
+static void __exit qi_lb60_exit(void)
+{
+ gpio_free(QI_LB60_AMP_GPIO);
+ gpio_free(QI_LB60_SND_GPIO);
+ platform_device_unregister(qi_lb60_snd_device);
+}
+module_exit(qi_lb60_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_DESCRIPTION("ALSA SoC QI LB60 Audio support");
+MODULE_LICENSE("GPL v2");
--
1.5.6.5
This patch adds a defconfig for the qi_lb60 clammshell device.
Signed-off-by: Lars-Peter Clausen <[email protected]>
---
arch/mips/configs/qi_lb60_defconfig | 1456 +++++++++++++++++++++++++++++++++++
1 files changed, 1456 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/configs/qi_lb60_defconfig
diff --git a/arch/mips/configs/qi_lb60_defconfig b/arch/mips/configs/qi_lb60_defconfig
new file mode 100644
index 0000000..74efbe0
--- /dev/null
+++ b/arch/mips/configs/qi_lb60_defconfig
@@ -0,0 +1,1456 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.35-rc1
+# Wed Jun 2 20:58:34 2010
+#
+CONFIG_MIPS=y
+
+#
+# Machine selection
+#
+# CONFIG_MACH_ALCHEMY is not set
+# CONFIG_AR7 is not set
+# CONFIG_BCM47XX is not set
+# CONFIG_BCM63XX is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_MACH_DECSTATION is not set
+# CONFIG_MACH_JAZZ is not set
+CONFIG_MACH_JZ4740=y
+# CONFIG_LASAT is not set
+# CONFIG_MACH_LOONGSON is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_SIM is not set
+# CONFIG_NEC_MARKEINS is not set
+# CONFIG_MACH_VR41XX is not set
+# CONFIG_NXP_STB220 is not set
+# CONFIG_NXP_STB225 is not set
+# CONFIG_PNX8550_JBS is not set
+# CONFIG_PNX8550_STB810 is not set
+# CONFIG_PMC_MSP is not set
+# CONFIG_PMC_YOSEMITE is not set
+# CONFIG_POWERTV is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SGI_IP27 is not set
+# CONFIG_SGI_IP28 is not set
+# CONFIG_SGI_IP32 is not set
+# CONFIG_SIBYTE_CRHINE is not set
+# CONFIG_SIBYTE_CARMEL is not set
+# CONFIG_SIBYTE_CRHONE is not set
+# CONFIG_SIBYTE_RHONE is not set
+# CONFIG_SIBYTE_SWARM is not set
+# CONFIG_SIBYTE_LITTLESUR is not set
+# CONFIG_SIBYTE_SENTOSA is not set
+# CONFIG_SIBYTE_BIGSUR is not set
+# CONFIG_SNI_RM is not set
+# CONFIG_MACH_TX39XX is not set
+# CONFIG_MACH_TX49XX is not set
+# CONFIG_MIKROTIK_RB532 is not set
+# CONFIG_WR_PPMC is not set
+# CONFIG_CAVIUM_OCTEON_SIMULATOR is not set
+# CONFIG_CAVIUM_OCTEON_REFERENCE_BOARD is not set
+# CONFIG_ALCHEMY_GPIO_INDIRECT is not set
+CONFIG_JZ4740_QI_LB60=y
+CONFIG_HAVE_PWM=y
+CONFIG_LOONGSON_UART_BASE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_SUPPORTS_OPROFILE=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_DMA_NONCOHERENT=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_SYS_HAS_EARLY_PRINTK=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_GPIO=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
+CONFIG_IRQ_CPU=y
+CONFIG_MIPS_L1_CACHE_SHIFT=5
+
+#
+# CPU selection
+#
+# CONFIG_CPU_LOONGSON2E is not set
+# CONFIG_CPU_LOONGSON2F is not set
+CONFIG_CPU_MIPS32_R1=y
+# CONFIG_CPU_MIPS32_R2 is not set
+# CONFIG_CPU_MIPS64_R1 is not set
+# CONFIG_CPU_MIPS64_R2 is not set
+# CONFIG_CPU_R3000 is not set
+# CONFIG_CPU_TX39XX is not set
+# CONFIG_CPU_VR41XX is not set
+# CONFIG_CPU_R4300 is not set
+# CONFIG_CPU_R4X00 is not set
+# CONFIG_CPU_TX49XX is not set
+# CONFIG_CPU_R5000 is not set
+# CONFIG_CPU_R5432 is not set
+# CONFIG_CPU_R5500 is not set
+# CONFIG_CPU_R6000 is not set
+# CONFIG_CPU_NEVADA is not set
+# CONFIG_CPU_R8000 is not set
+# CONFIG_CPU_R10000 is not set
+# CONFIG_CPU_RM7000 is not set
+# CONFIG_CPU_RM9000 is not set
+# CONFIG_CPU_SB1 is not set
+# CONFIG_CPU_CAVIUM_OCTEON is not set
+CONFIG_SYS_HAS_CPU_MIPS32_R1=y
+CONFIG_CPU_MIPS32=y
+CONFIG_CPU_MIPSR1=y
+CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
+CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
+CONFIG_HARDWARE_WATCHPOINTS=y
+
+#
+# Kernel type
+#
+CONFIG_32BIT=y
+# CONFIG_64BIT is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_32KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_CPU_HAS_PREFETCH=y
+CONFIG_MIPS_MT_DISABLED=y
+# CONFIG_MIPS_MT_SMP is not set
+# CONFIG_MIPS_MT_SMTC is not set
+# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set
+CONFIG_CPU_HAS_SYNC=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_CPU_SUPPORTS_HIGHMEM=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+# CONFIG_HZ_48 is not set
+CONFIG_HZ_100=y
+# CONFIG_HZ_128 is not set
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_256 is not set
+# CONFIG_HZ_1000 is not set
+# CONFIG_HZ_1024 is not set
+CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
+CONFIG_HZ=100
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+# CONFIG_KEXEC is not set
+# CONFIG_SECCOMP is not set
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+# CONFIG_TREE_RCU is not set
+# CONFIG_TREE_PREEMPT_RCU is not set
+CONFIG_TINY_RCU=y
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_EMBEDDED=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+# CONFIG_PCSPKR_PLATFORM is not set
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Kernel Performance Events And Counters
+#
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+CONFIG_HAVE_OPROFILE=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+# CONFIG_INLINE_SPIN_TRYLOCK is not set
+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK is not set
+# CONFIG_INLINE_SPIN_LOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
+# CONFIG_INLINE_SPIN_UNLOCK is not set
+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
+# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_READ_TRYLOCK is not set
+# CONFIG_INLINE_READ_LOCK is not set
+# CONFIG_INLINE_READ_LOCK_BH is not set
+# CONFIG_INLINE_READ_LOCK_IRQ is not set
+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
+# CONFIG_INLINE_READ_UNLOCK is not set
+# CONFIG_INLINE_READ_UNLOCK_BH is not set
+# CONFIG_INLINE_READ_UNLOCK_IRQ is not set
+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_WRITE_TRYLOCK is not set
+# CONFIG_INLINE_WRITE_LOCK is not set
+# CONFIG_INLINE_WRITE_LOCK_BH is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
+# CONFIG_INLINE_WRITE_UNLOCK is not set
+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
+# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
+# CONFIG_MUTEX_SPIN_ON_OWNER is not set
+CONFIG_FREEZER=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, ISA, TC)
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_MMU=y
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_TRAD_SIGNALS=y
+
+#
+# Power management options
+#
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+# CONFIG_HIBERNATION is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_PM_OPS=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+CONFIG_IP_MROUTE=y
+# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set
+# CONFIG_IP_PIMSM_V1 is not set
+# CONFIG_IP_PIMSM_V2 is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BIC is not set
+# CONFIG_TCP_CONG_CUBIC is not set
+CONFIG_TCP_CONG_WESTWOOD=y
+# CONFIG_TCP_CONG_HTCP is not set
+# CONFIG_TCP_CONG_HSTCP is not set
+# CONFIG_TCP_CONG_HYBLA is not set
+# CONFIG_TCP_CONG_VEGAS is not set
+# CONFIG_TCP_CONG_SCALABLE is not set
+# CONFIG_TCP_CONG_LP is not set
+# CONFIG_TCP_CONG_VENO is not set
+# CONFIG_TCP_CONG_YEAH is not set
+# CONFIG_TCP_CONG_ILLINOIS is not set
+# CONFIG_DEFAULT_BIC is not set
+# CONFIG_DEFAULT_CUBIC is not set
+# CONFIG_DEFAULT_HTCP is not set
+# CONFIG_DEFAULT_HYBLA is not set
+# CONFIG_DEFAULT_VEGAS is not set
+# CONFIG_DEFAULT_VENO is not set
+CONFIG_DEFAULT_WESTWOOD=y
+# CONFIG_DEFAULT_RENO is not set
+CONFIG_DEFAULT_TCP_CONG="westwood"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NF_CONNTRACK is not set
+# CONFIG_NETFILTER_XTABLES is not set
+# CONFIG_IP_VS is not set
+
+#
+# IP: Netfilter Configuration
+#
+# CONFIG_NF_DEFRAG_IPV4 is not set
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+CONFIG_ATM=m
+# CONFIG_ATM_CLIP is not set
+# CONFIG_ATM_LANE is not set
+CONFIG_ATM_BR2684=m
+CONFIG_ATM_BR2684_IPFILTER=y
+# CONFIG_L2TP is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+# CONFIG_LIB80211 is not set
+
+#
+# CFG80211 needs to be enabled for MAC80211
+#
+
+#
+# Some wireless drivers require a rate control algorithm
+#
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR=0xFF108018
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+CONFIG_MTD_NAND_JZ4740=y
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_RESERVE=1
+# CONFIG_MTD_UBI_GLUEBI is not set
+
+#
+# UBI debugging options
+#
+# CONFIG_MTD_UBI_DEBUG is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+
+#
+# DRBD disabled because PROC_FS, INET or CONNECTOR not selected
+#
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_TI_DAC7512 is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_IWMC3200TOP is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_NET_ETHERNET is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+CONFIG_WLAN=y
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_HOSTAP is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_IPHETH is not set
+# CONFIG_WAN is not set
+# CONFIG_ATM_DRIVERS is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+CONFIG_ISDN=y
+# CONFIG_ISDN_I4L is not set
+# CONFIG_ISDN_CAPI is not set
+# CONFIG_ISDN_DRV_GIGASET is not set
+# CONFIG_MISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_MATRIX=y
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_DEVKMEM is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_N_GSM is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=2
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_RAMOOPS is not set
+# CONFIG_I2C is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_GPIO=y
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO expanders:
+#
+# CONFIG_GPIO_IT8761E is not set
+
+#
+# I2C GPIO expanders:
+#
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
+
+#
+# AC97 GPIO expanders:
+#
+
+#
+# MODULbus GPIO expanders:
+#
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
+CONFIG_BATTERY_JZ4740=y
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Native drivers
+#
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_IT87 is not set
+CONFIG_SENSORS_JZ4740=y
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_MAX1111 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_SHT15 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_ADS7871 is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+# CONFIG_MFD_SUPPORT is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+# CONFIG_FB_CFB_FILLRECT is not set
+# CONFIG_FB_CFB_COPYAREA is not set
+# CONFIG_FB_CFB_IMAGEBLIT is not set
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+CONFIG_FB_SYS_FILLRECT=y
+CONFIG_FB_SYS_COPYAREA=y
+CONFIG_FB_SYS_IMAGEBLIT=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+CONFIG_FB_JZ4740=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_L4F00242T03 is not set
+# CONFIG_LCD_LMS283GF05 is not set
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+# CONFIG_LCD_PLATFORM is not set
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+CONFIG_FONT_SUN8x16=y
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_SOUND=m
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SOUND_OSS_CORE_PRECLAIM=y
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_JACK=y
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_SEQUENCER_OSS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+# CONFIG_SND_SUPPORT_OLD_API is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_RAWMIDI_SEQ=m
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+CONFIG_SND_VIRMIDI=m
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_MIPS is not set
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=m
+CONFIG_SND_SOC_I2C_AND_SPI=m
+# CONFIG_SND_SOC_ALL_CODECS is not set
+# CONFIG_SOUND_PRIME is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DEVICE_CLASS is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_ATM is not set
+# CONFIG_USB_GADGET is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_JZ4740=y
+# CONFIG_MMC_SPI is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_DAC124S085 is not set
+CONFIG_LEDS_PWM=y
+# CONFIG_LEDS_LT3593 is not set
+CONFIG_LEDS_TRIGGERS=y
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+# CONFIG_LEDS_TRIGGER_GPIO is not set
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+
+#
+# iptables trigger is under Netfilter config (LED target)
+#
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_JZ4740=y
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+# CONFIG_PROC_PAGE_MONITOR is not set
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+# CONFIG_JFFS2_ZLIB is not set
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_XATTR is not set
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+# CONFIG_UBIFS_FS_DEBUG is not set
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=y
+CONFIG_NLS_CODEPAGE_775=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_CODEPAGE_852=y
+CONFIG_NLS_CODEPAGE_855=y
+CONFIG_NLS_CODEPAGE_857=y
+CONFIG_NLS_CODEPAGE_860=y
+CONFIG_NLS_CODEPAGE_861=y
+CONFIG_NLS_CODEPAGE_862=y
+CONFIG_NLS_CODEPAGE_863=y
+CONFIG_NLS_CODEPAGE_864=y
+CONFIG_NLS_CODEPAGE_865=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_869=y
+CONFIG_NLS_CODEPAGE_936=y
+CONFIG_NLS_CODEPAGE_950=y
+CONFIG_NLS_CODEPAGE_932=y
+CONFIG_NLS_CODEPAGE_949=y
+CONFIG_NLS_CODEPAGE_874=y
+CONFIG_NLS_ISO8859_8=y
+CONFIG_NLS_CODEPAGE_1250=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=y
+CONFIG_NLS_ISO8859_3=y
+CONFIG_NLS_ISO8859_4=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_ISO8859_6=y
+CONFIG_NLS_ISO8859_7=y
+CONFIG_NLS_ISO8859_9=y
+CONFIG_NLS_ISO8859_13=y
+CONFIG_NLS_ISO8859_14=y
+CONFIG_NLS_ISO8859_15=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_KOI8_U=y
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_PRINTK_TIME=y
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_STRIP_ASM_SYMS=y
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_LKDTM is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_EARLY_PRINTK=y
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_SPINLOCK_TEST is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_DEFAULT_SECURITY_SELINUX is not set
+# CONFIG_DEFAULT_SECURITY_SMACK is not set
+# CONFIG_DEFAULT_SECURITY_TOMOYO is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_PCOMP=y
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_MANAGER2 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_ZLIB=y
+CONFIG_CRYPTO_LZO=y
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC16=y
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
--
1.5.6.5
On Wed, 2 Jun 2010 21:10:32 +0200
Lars-Peter Clausen <[email protected]> wrote:
> This patch adds support for the LCD controller on JZ4740 SoCs.
>
>
> ...
>
> +struct jzfb_framedesc {
> + uint32_t next;
> + uint32_t addr;
> + uint32_t id;
> + uint32_t cmd;
> +} __attribute__((packed));
We have a little __packed helper.
>
> ...
>
> +static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green,
> + unsigned blue, unsigned transp, struct fb_info *fb)
> +{
> + uint32_t color;
> +
> + if (regno >= 16)
> + return -EINVAL;
> +
> + color = jzfb_convert_color_to_hw(red, fb->var.red.length);
> + color |= jzfb_convert_color_to_hw(green, fb->var.green.length);
> + color |= jzfb_convert_color_to_hw(blue, fb->var.blue.length);
> + color |= jzfb_convert_color_to_hw(transp, fb->var.transp.length);
> +
> + ((uint32_t *)(fb->pseudo_palette))[regno] = color;
The typecast apepars to be unneeded. If it _was_ needed then this code
would look rather fishy from an endianness POV.
> + return 0;
> +}
> +
>
> ...
>
> +static int __devinit jzfb_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct jzfb *jzfb;
> + struct fb_info *fb;
> + struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data;
> + struct resource *mem;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "Missing platform data\n");
> + return -ENOENT;
> + }
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "Failed to get register memory resource\n");
> + return -ENOENT;
> + }
> +
> + mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
> + if (!mem) {
> + dev_err(&pdev->dev, "Failed to request register memory region\n");
> + return -EBUSY;
> + }
> +
> + fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev);
> + if (!fb) {
> + dev_err(&pdev->dev, "Failed to allocate framebuffer device\n");
> + ret = -ENOMEM;
> + goto err_release_mem_region;
> + }
> +
> + fb->fbops = &jzfb_ops;
> + fb->flags = FBINFO_DEFAULT;
> +
> + jzfb = fb->par;
> + jzfb->pdev = pdev;
> + jzfb->pdata = pdata;
> + jzfb->mem = mem;
> +
> + jzfb->ldclk = clk_get(&pdev->dev, "lcd");
> + if (IS_ERR(jzfb->ldclk)) {
> + ret = PTR_ERR(jzfb->ldclk);
> + dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret);
> + goto err_framebuffer_release;
> + }
> +
> + jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk");
> + if (IS_ERR(jzfb->lpclk)) {
> + ret = PTR_ERR(jzfb->lpclk);
> + dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret);
> + goto err_put_ldclk;
> + }
> +
> + jzfb->base = ioremap(mem->start, resource_size(mem));
> +
> + if (!jzfb->base) {
> + dev_err(&pdev->dev, "Failed to ioremap register memory region\n");
> + ret = -EBUSY;
> + goto err_put_lpclk;
> + }
> +
> + platform_set_drvdata(pdev, jzfb);
> +
> + fb_videomode_to_modelist(pdata->modes, pdata->num_modes,
> + &fb->modelist);
> + fb->mode = pdata->modes;
> +
> + fb_videomode_to_var(&fb->var, fb->mode);
> + fb->var.bits_per_pixel = pdata->bpp;
> + jzfb_check_var(&fb->var, fb);
> +
> + ret = jzfb_alloc_devmem(jzfb);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to allocate video memory\n");
> + goto err_iounmap;
> + }
> +
> + fb->fix = jzfb_fix;
> + fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
> + fb->fix.mmio_start = mem->start;
> + fb->fix.mmio_len = resource_size(mem);
> + fb->fix.smem_start = jzfb->vidmem_phys;
> + fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
> + fb->screen_base = jzfb->vidmem;
> + fb->pseudo_palette = jzfb->pseudo_palette;
> +
> + fb_alloc_cmap(&fb->cmap, 256, 0);
> +
> + mutex_init(&jzfb->lock);
I'd suggest initializing the mutex much earlier. THings like
jzfb_check_var() and jzfb_alloc_devmem() don't currently take that
lock, but who knows what might happen in the future..
> + clk_enable(jzfb->ldclk);
> + jzfb->is_enabled = 1;
> +
> + writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
> + jzfb_set_par(fb);
> +
> + jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
> + jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
> +
> + ret = register_framebuffer(fb);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret);
> + goto err_free_devmem;
> + }
> +
> + jzfb->fb = fb;
> +
> + return 0;
> +
> +err_free_devmem:
> + jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
> + jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
> +
> + fb_dealloc_cmap(&fb->cmap);
> + jzfb_free_devmem(jzfb);
> +err_iounmap:
> + iounmap(jzfb->base);
> +err_put_lpclk:
> + clk_put(jzfb->lpclk);
> +err_put_ldclk:
> + clk_put(jzfb->ldclk);
> +err_framebuffer_release:
> + framebuffer_release(fb);
> +err_release_mem_region:
> + release_mem_region(mem->start, resource_size(mem));
> + return ret;
> +}
> +
>
> ...
>
Andrew Morton wrote:
> On Wed, 2 Jun 2010 21:10:32 +0200
> Lars-Peter Clausen <[email protected]> wrote:
>
>
>> This patch adds support for the LCD controller on JZ4740 SoCs.
>>
>>
>> ...
>>
>> +struct jzfb_framedesc {
>> + uint32_t next;
>> + uint32_t addr;
>> + uint32_t id;
>> + uint32_t cmd;
>> +} __attribute__((packed));
>>
>
> We have a little __packed helper.
>
>
Ok.
>> ...
>>
>> +static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green,
>> + unsigned blue, unsigned transp, struct fb_info *fb)
>> +{
>> + uint32_t color;
>> +
>> + if (regno >= 16)
>> + return -EINVAL;
>> +
>> + color = jzfb_convert_color_to_hw(red, fb->var.red.length);
>> + color |= jzfb_convert_color_to_hw(green, fb->var.green.length);
>> + color |= jzfb_convert_color_to_hw(blue, fb->var.blue.length);
>> + color |= jzfb_convert_color_to_hw(transp, fb->var.transp.length);
>> +
>> + ((uint32_t *)(fb->pseudo_palette))[regno] = color;
>>
>
> The typecast apepars to be unneeded. If it _was_ needed then this code
> would look rather fishy from an endianness POV.
>
>
It is needed, because the pseudo_palette member of struct fb_info is a
void pointer.
>> + return 0;
>> +}
>> +
>>
>> ...
>>
>> +static int __devinit jzfb_probe(struct platform_device *pdev)
>> +{
>> + int ret;
>> + struct jzfb *jzfb;
>> + struct fb_info *fb;
>> + struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data;
>> + struct resource *mem;
>> +
>> + if (!pdata) {
>> + dev_err(&pdev->dev, "Missing platform data\n");
>> + return -ENOENT;
>> + }
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "Failed to get register memory resource\n");
>> + return -ENOENT;
>> + }
>> +
>> + mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "Failed to request register memory region\n");
>> + return -EBUSY;
>> + }
>> +
>> + fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev);
>> + if (!fb) {
>> + dev_err(&pdev->dev, "Failed to allocate framebuffer device\n");
>> + ret = -ENOMEM;
>> + goto err_release_mem_region;
>> + }
>> +
>> + fb->fbops = &jzfb_ops;
>> + fb->flags = FBINFO_DEFAULT;
>> +
>> + jzfb = fb->par;
>> + jzfb->pdev = pdev;
>> + jzfb->pdata = pdata;
>> + jzfb->mem = mem;
>> +
>> + jzfb->ldclk = clk_get(&pdev->dev, "lcd");
>> + if (IS_ERR(jzfb->ldclk)) {
>> + ret = PTR_ERR(jzfb->ldclk);
>> + dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret);
>> + goto err_framebuffer_release;
>> + }
>> +
>> + jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk");
>> + if (IS_ERR(jzfb->lpclk)) {
>> + ret = PTR_ERR(jzfb->lpclk);
>> + dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret);
>> + goto err_put_ldclk;
>> + }
>> +
>> + jzfb->base = ioremap(mem->start, resource_size(mem));
>> +
>> + if (!jzfb->base) {
>> + dev_err(&pdev->dev, "Failed to ioremap register memory region\n");
>> + ret = -EBUSY;
>> + goto err_put_lpclk;
>> + }
>> +
>> + platform_set_drvdata(pdev, jzfb);
>> +
>> + fb_videomode_to_modelist(pdata->modes, pdata->num_modes,
>> + &fb->modelist);
>> + fb->mode = pdata->modes;
>> +
>> + fb_videomode_to_var(&fb->var, fb->mode);
>> + fb->var.bits_per_pixel = pdata->bpp;
>> + jzfb_check_var(&fb->var, fb);
>> +
>> + ret = jzfb_alloc_devmem(jzfb);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Failed to allocate video memory\n");
>> + goto err_iounmap;
>> + }
>> +
>> + fb->fix = jzfb_fix;
>> + fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
>> + fb->fix.mmio_start = mem->start;
>> + fb->fix.mmio_len = resource_size(mem);
>> + fb->fix.smem_start = jzfb->vidmem_phys;
>> + fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
>> + fb->screen_base = jzfb->vidmem;
>> + fb->pseudo_palette = jzfb->pseudo_palette;
>> +
>> + fb_alloc_cmap(&fb->cmap, 256, 0);
>> +
>> + mutex_init(&jzfb->lock);
>>
>
> I'd suggest initializing the mutex much earlier. THings like
> jzfb_check_var() and jzfb_alloc_devmem() don't currently take that
> lock, but who knows what might happen in the future..
>
>
Hm, ok.
>> + clk_enable(jzfb->ldclk);
>> + jzfb->is_enabled = 1;
>> +
>> + writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
>> + jzfb_set_par(fb);
>> +
>> + jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
>> + jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
>> +
>> + ret = register_framebuffer(fb);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret);
>> + goto err_free_devmem;
>> + }
>> +
>> + jzfb->fb = fb;
>> +
>> + return 0;
>> +
>> +err_free_devmem:
>> + jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
>> + jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
>> +
>> + fb_dealloc_cmap(&fb->cmap);
>> + jzfb_free_devmem(jzfb);
>> +err_iounmap:
>> + iounmap(jzfb->base);
>> +err_put_lpclk:
>> + clk_put(jzfb->lpclk);
>> +err_put_ldclk:
>> + clk_put(jzfb->ldclk);
>> +err_framebuffer_release:
>> + framebuffer_release(fb);
>> +err_release_mem_region:
>> + release_mem_region(mem->start, resource_size(mem));
>> + return ret;
>> +}
>> +
>>
>> ...
>>
>>
>
>
On 3 June 2010 04:32, Lars-Peter Clausen <[email protected]> wrote:
> + ? ? ? {
> + ? ? ? ? ? ? ? .name = "dma",
> + ? ? ? ? ? ? ? .parent = &jz_clk_high_speed_peripheral.clk,
> + ? ? ? ? ? ? ? .gate_bit = JZ_CLOCK_GATE_UART0,
> + ? ? ? ? ? ? ? .ops = &jz_clk_simple_ops,
> + ? ? ? },
Presumably this should be JZ_CLOCK_GATE_DMAC.
-Graham
Hi,
I have some minor comments below:
> This patch adds ASoC support for JZ4740 SoCs I2S module.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Liam Girdwood <[email protected]>
> Cc: [email protected]
> ---
> sound/soc/Kconfig | 1 +
> sound/soc/Makefile | 1 +
> sound/soc/jz4740/Kconfig | 13 +
> sound/soc/jz4740/Makefile | 9 +
> sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-i2s.h | 18 ++
> sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-pcm.h | 22 ++
> 8 files changed, 982 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/jz4740/Kconfig
> create mode 100644 sound/soc/jz4740/Makefile
> create mode 100644 sound/soc/jz4740/jz4740-i2s.c
> create mode 100644 sound/soc/jz4740/jz4740-i2s.h
> create mode 100644 sound/soc/jz4740/jz4740-pcm.c
> create mode 100644 sound/soc/jz4740/jz4740-pcm.h
>
> diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
> index b1749bc..5a7a724 100644
> --- a/sound/soc/Kconfig
> +++ b/sound/soc/Kconfig
> @@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
> source "sound/soc/s6000/Kconfig"
> source "sound/soc/sh/Kconfig"
> source "sound/soc/txx9/Kconfig"
> +source "sound/soc/jz4740/Kconfig"
>
> # Supported codecs
> source "sound/soc/codecs/Kconfig"
> diff --git a/sound/soc/Makefile b/sound/soc/Makefile
> index 1470141..fdbe74d 100644
> --- a/sound/soc/Makefile
> +++ b/sound/soc/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
> obj-$(CONFIG_SND_SOC) += s6000/
> obj-$(CONFIG_SND_SOC) += sh/
> obj-$(CONFIG_SND_SOC) += txx9/
> +obj-$(CONFIG_SND_SOC) += jz4740/
should keep the Kconfig and Makefile sorted, this helps avoid merge
issues.
except above issue, Also, I see many warning infos when using checkpatch tool,as following:
WARNING: line over 80 characters
#358: FILE: sound/soc/jz4740/jz4740-i2s.c:204:
+ conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
WARNING: line over 80 characters
#536: FILE: sound/soc/jz4740/jz4740-i2s.c:382:
+static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
WARNING: EXPORT_SYMBOL(foo); should immediately follow its function/variable
#587: FILE: sound/soc/jz4740/jz4740-i2s.c:433:
+EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
WARNING: line over 80 characters
#813: FILE: sound/soc/jz4740/jz4740-pcm.c:61:
+static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
WARNING: EXPORT_SYMBOL(foo); should immediately follow its function/variable
#1086: FILE: sound/soc/jz4740/jz4740-pcm.c:334:
+EXPORT_SYMBOL_GPL(jz4740_soc_platform);
total: 0 errors, 5 warnings, 991 lines checked
> diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
> new file mode 100644
> index 0000000..39df949
> --- /dev/null
> +++ b/sound/soc/jz4740/Kconfig
> @@ -0,0 +1,13 @@
> +config SND_JZ4740_SOC
> + tristate "SoC Audio for Ingenic JZ4740 SoC"
> + depends on SOC_JZ4740 && SND_SOC
> + help
> + Say Y or M if you want to add support for codecs attached to
> + the Jz4740 AC97, I2S or SSP interface. You will also need
> + to select the audio interfaces to support below.
> +
> +config SND_JZ4740_SOC_I2S
> + depends on SND_JZ4740_SOC
> + tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
> + help
> + Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
> diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
> new file mode 100644
> index 0000000..1be8d19
> --- /dev/null
> +++ b/sound/soc/jz4740/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Jz4740 Platform Support
> +#
> +snd-soc-jz4740-objs := jz4740-pcm.o
> +snd-soc-jz4740-i2s-objs := jz4740-i2s.o
> +
> +obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
> +obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
> +
> diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
> new file mode 100644
> index 0000000..2b139fd
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.c
> @@ -0,0 +1,568 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/initval.h>
> +
> +#include "jz4740-i2s.h"
> +#include "jz4740-pcm.h"
> +
> +#define JZ_REG_AIC_CONF 0x00
> +#define JZ_REG_AIC_CTRL 0x04
> +#define JZ_REG_AIC_I2S_FMT 0x10
> +#define JZ_REG_AIC_FIFO_STATUS 0x14
> +#define JZ_REG_AIC_I2S_STATUS 0x1c
> +#define JZ_REG_AIC_CLK_DIV 0x30
> +#define JZ_REG_AIC_FIFO 0x34
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
> +#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
> +#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
> +#define JZ_AIC_CONF_I2S BIT(4)
> +#define JZ_AIC_CONF_RESET BIT(3)
> +#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
> +#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
> +#define JZ_AIC_CONF_ENABLE BIT(0)
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
> +#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
> +#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
> +#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
> +#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
> +#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
> +#define JZ_AIC_CTRL_FLUSH BIT(8)
> +#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
> +#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
> +#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
> +#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
> +#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
> +#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
> +#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
> +
> +#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
> +#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
> +#define JZ_AIC_I2S_FMT_MSB BIT(0)
> +
> +#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
> +
> +#define JZ_AIC_CLK_DIV_MASK 0xf
> +
> +struct jz4740_i2s {
> + struct resource *mem;
> + void __iomem *base;
> + dma_addr_t phys_base;
> +
> + struct clk *clk_aic;
> + struct clk *clk_i2s;
> +
> + struct jz4740_pcm_config pcm_config_playback;
> + struct jz4740_pcm_config pcm_config_capture;
> +};
> +
> +static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
> + unsigned int reg)
> +{
> + return readl(i2s->base + reg);
> +}
> +
> +static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
> + unsigned int reg, uint32_t value)
> +{
> + writel(value, i2s->base + reg);
> +}
> +
> +static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
> +{
> + return dai->private_data;
> +}
> +
> +static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf, ctrl;
> +
> + if (dai->active)
> + return 0;
> +
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> + ctrl |= JZ_AIC_CTRL_FLUSH;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (!dai->active)
> + return;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> +}
> +
> +
> +static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +
> + uint32_t ctrl;
> + uint32_t mask;
> +
> + if (playback)
> + mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
> + else
> + mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ctrl |= mask;
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + ctrl &= ~mask;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + return 0;
> +}
> +
> +
> +static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + uint32_t format = 0;
> + uint32_t conf;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> +
> + conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
> +
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBS_CFS:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
> + format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFS:
> + conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBS_CFM:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_MSB:
> + format |= JZ_AIC_I2S_FMT_MSB;
> + break;
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> + enum jz4740_dma_width dma_width;
> + struct jz4740_pcm_config *pcm_config;
> + unsigned int sample_size;
> + uint32_t ctrl;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + sample_size = 0;
> + dma_width = JZ4740_DMA_WIDTH_8BIT;
> + break;
> + case SNDRV_PCM_FORMAT_S16:
> + sample_size = 1;
> + dma_width = JZ4740_DMA_WIDTH_16BIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (playback) {
> + ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
> + } else {
> + ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
> + }
> +
> + switch (params_channels(params)) {
> + case 2:
> + break;
> + case 1:
> + if (playback) {
> + ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
> + break;
> + }
> + default: /* Falltrough */
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + if (playback) {
> + pcm_config = &i2s->pcm_config_playback;
> + pcm_config->dma_config.dst_width = dma_width;
> + } else {
> + pcm_config = &i2s->pcm_config_capture;
> + pcm_config->dma_config.src_width = dma_width;
> + }
> +
> +
> + snd_soc_dai_set_dma_data(dai, substream, pcm_config);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + switch (div_id) {
> + case JZ4740_I2S_BIT_CLK:
> + if (div & 1 || div > 16)
> + return -EINVAL;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + int ret = 0;
> + struct clk *parent;
> +
> + switch (clk_id) {
> + case JZ4740_I2S_CLKSRC_EXT:
> + parent = clk_get(NULL, "ext");
> + clk_set_parent(i2s->clk_i2s, parent);
> + break;
> + case JZ4740_I2S_CLKSRC_PLL:
> + parent = clk_get(NULL, "pll half");
> + clk_set_parent(i2s->clk_i2s, parent);
> + ret = clk_set_rate(i2s->clk_i2s, freq);
> + break;
> + default:
> + return -EINVAL;
> + }
> + clk_put(parent);
> +
> + return ret;
> +}
> +
> +static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (dai->active) {
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> + }
> +
> + clk_disable(i2s->clk_aic);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_resume(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + clk_enable(i2s->clk_aic);
> +
> + if (dai->active) {
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
> + (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
> + JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
> + JZ_AIC_CONF_I2S |
> + JZ_AIC_CONF_INTERNAL_CODEC;
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
> + .startup = jz4740_i2s_startup,
> + .shutdown = jz4740_i2s_shutdown,
> + .trigger = jz4740_i2s_trigger,
> + .hw_params = jz4740_i2s_hw_params,
> + .set_fmt = jz4740_i2s_set_fmt,
> + .set_clkdiv = jz4740_i2s_set_clkdiv,
> + .set_sysclk = jz4740_i2s_set_sysclk,
> +};
> +
> +#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
> + SNDRV_PCM_FMTBIT_S16_LE)
> +
> +struct snd_soc_dai jz4740_i2s_dai = {
> + .name = "jz4740-i2s",
> + .probe = jz4740_i2s_probe,
> + .playback = {
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .capture = {
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .symmetric_rates = 1,
> + .ops = &jz4740_i2s_dai_ops,
> + .suspend = jz4740_i2s_suspend,
> + .resume = jz4740_i2s_resume,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
> +
> +static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
> +{
> + struct jz4740_dma_config *dma_config;
> +
> + /* Playback */
> + dma_config = &i2s->pcm_config_playback.dma_config;
> + dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
> + dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +
> + /* Capture */
> + dma_config = &i2s->pcm_config_capture.dma_config;
> + dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
> + dma_config->flags = JZ4740_DMA_DST_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +}
> +
> +static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s;
> + int ret;
> +
> + i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
> +
> + if (!i2s)
> + return -ENOMEM;
> +
> + i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!i2s->mem) {
> + ret = -ENOENT;
> + goto err_free;
> + }
> +
> + i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
> + pdev->name);
> +
> + if (!i2s->mem) {
> + ret = -EBUSY;
> + goto err_free;
> + }
> +
> + i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
> +
> + if (!i2s->base) {
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + i2s->phys_base = i2s->mem->start;
> +
> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
> + if (IS_ERR(i2s->clk_aic)) {
> + ret = PTR_ERR(i2s->clk_aic);
> + goto err_iounmap;
> + }
> +
> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
> + if (IS_ERR(i2s->clk_i2s)) {
> + ret = PTR_ERR(i2s->clk_i2s);
> + goto err_iounmap;
> + }
> +
> + clk_enable(i2s->clk_aic);
> +
> + jz4740_i2c_init_pcm_config(i2s);
> +
> + jz4740_i2s_dai.private_data = i2s;
> + ret = snd_soc_register_dai(&jz4740_i2s_dai);
> +
> + platform_set_drvdata(pdev, i2s);
> +
> + return 0;
> +
> +err_iounmap:
> + iounmap(i2s->base);
> +err_release_mem_region:
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +err_free:
> + kfree(i2s);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_i2s_dai);
> +
> + clk_disable(i2s->clk_aic);
> + clk_put(i2s->clk_i2s);
> + clk_put(i2s->clk_aic);
> +
> + iounmap(i2s->base);
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(i2s);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_i2s_driver = {
> + .probe = jz4740_i2s_dev_probe,
> + .remove = __devexit_p(jz4740_i2s_dev_remove),
> + .driver = {
> + .name = "jz4740-i2s",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_i2s_init(void)
> +{
> + return platform_driver_register(&jz4740_i2s_driver);
> +}
> +module_init(jz4740_i2s_init);
> +
> +static void __exit jz4740_i2s_exit(void)
> +{
> + platform_driver_unregister(&jz4740_i2s_driver);
> +}
> +module_exit(jz4740_i2s_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen, <[email protected]>");
> +MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:jz4740-i2s");
> diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
> new file mode 100644
> index 0000000..da22ed8
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.h
> @@ -0,0 +1,18 @@
> +/*
> + * 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.
> + */
> +
> +#ifndef _JZ4740_I2S_H
> +#define _JZ4740_I2S_H
> +
> +/* I2S clock source */
> +#define JZ4740_I2S_CLKSRC_EXT 0
> +#define JZ4740_I2S_CLKSRC_PLL 1
> +
> +#define JZ4740_I2S_BIT_CLK 0
> +
> +extern struct snd_soc_dai jz4740_i2s_dai;
> +
> +#endif
> diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
> new file mode 100644
> index 0000000..fd1c203
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.c
> @@ -0,0 +1,350 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +
> +#include <asm/mach-jz4740/dma.h>
> +#include "jz4740-pcm.h"
> +
> +struct jz4740_runtime_data {
> + unsigned int dma_period;
> + dma_addr_t dma_start;
> + dma_addr_t dma_pos;
> + dma_addr_t dma_end;
> +
> + struct jz4740_dma_chan *dma;
> +
> + dma_addr_t fifo_addr;
> +};
> +
> +/* identify hardware playback capabilities */
> +static const struct snd_pcm_hardware jz4740_pcm_hardware = {
> + .info = SNDRV_PCM_INFO_MMAP |
> + SNDRV_PCM_INFO_MMAP_VALID |
> + SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_BLOCK_TRANSFER,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> +
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .channels_min = 1,
> + .channels_max = 2,
> + .period_bytes_min = 16,
> + .period_bytes_max = 2 * PAGE_SIZE,
> + .periods_min = 2,
> + .periods_max = 128,
> + .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
> + .fifo_size = 32,
> +};
> +
> +static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
> +{
> + unsigned int count;
> +
> + if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
> + count = prtd->dma_end - prtd->dma_pos;
> + else
> + count = prtd->dma_period;
> +
> + jz4740_dma_disable(prtd->dma);
> +
> + if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
> + } else {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
> + }
> +
> + jz4740_dma_set_transfer_count(prtd->dma, count);
> +
> + jz4740_dma_enable(prtd->dma);
> +
> + prtd->dma_pos += prtd->dma_period;
> + if (prtd->dma_pos >= prtd->dma_end)
> + prtd->dma_pos = prtd->dma_start;
> +}
> +
> +static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
> + void *dev_id)
> +{
> + struct snd_pcm_substream *substream = dev_id;
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + snd_pcm_period_elapsed(substream);
> +
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> +}
> +
> +static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct jz4740_pcm_config *config;
> +
> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
> + if (!prtd->dma) {
> + const char *dma_channel_name;
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> + dma_channel_name = "PCM Playback";
> + else
> + dma_channel_name = "PCM Capture";
> +
> + prtd->dma = jz4740_dma_request(substream, dma_channel_name);
> + }
> +
> + if (!prtd->dma)
> + return -EBUSY;
> +
> + jz4740_dma_configure(prtd->dma, &config->dma_config);
> + prtd->fifo_addr = config->fifo_addr;
> +
> + jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
> +
> + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> + runtime->dma_bytes = params_buffer_bytes(params);
> +
> + prtd->dma_period = params_period_bytes(params);
> + prtd->dma_start = runtime->dma_addr;
> + prtd->dma_pos = prtd->dma_start;
> + prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> +
> + snd_pcm_set_runtime_buffer(substream, NULL);
> + if (prtd->dma) {
> + jz4740_dma_free(prtd->dma);
> + prtd->dma = NULL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> + int ret = 0;
> +
> + if (!prtd->dma)
> + return 0;
> +
> + prtd->dma_pos = prtd->dma_start;
> +
> + return ret;
> +}
> +
> +static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + int ret = 0;
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + jz4740_dma_disable(prtd->dma);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + unsigned long count, pos;
> + snd_pcm_uframes_t offset;
> + struct jz4740_dma_chan *dma = prtd->dma;
> +
> + count = jz4740_dma_get_residue(dma);
> + if (prtd->dma_pos == prtd->dma_start)
> + pos = prtd->dma_end - prtd->dma_start - count;
> + else
> + pos = prtd->dma_pos - prtd->dma_start - count;
> +
> + offset = bytes_to_frames(runtime, pos);
> + if (offset >= runtime->buffer_size)
> + offset = 0;
> +
> + return offset;
> +}
> +
> +static int jz4740_pcm_open(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd;
> +
> + snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
> + prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
> +
> + if (prtd == NULL)
> + return -ENOMEM;
> +
> + runtime->private_data = prtd;
> + return 0;
> +}
> +
> +static int jz4740_pcm_close(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + kfree(prtd);
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
> + struct vm_area_struct *vma)
> +{
> + return remap_pfn_range(vma, vma->vm_start,
> + substream->dma_buffer.addr >> PAGE_SHIFT,
> + vma->vm_end - vma->vm_start, vma->vm_page_prot);
> +}
> +
> +static struct snd_pcm_ops jz4740_pcm_ops = {
> + .open = jz4740_pcm_open,
> + .close = jz4740_pcm_close,
> + .ioctl = snd_pcm_lib_ioctl,
> + .hw_params = jz4740_pcm_hw_params,
> + .hw_free = jz4740_pcm_hw_free,
> + .prepare = jz4740_pcm_prepare,
> + .trigger = jz4740_pcm_trigger,
> + .pointer = jz4740_pcm_pointer,
> + .mmap = jz4740_pcm_mmap,
> +};
> +
> +static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
> +{
> + struct snd_pcm_substream *substream = pcm->streams[stream].substream;
> + struct snd_dma_buffer *buf = &substream->dma_buffer;
> + size_t size = jz4740_pcm_hardware.buffer_bytes_max;
> +
> + buf->dev.type = SNDRV_DMA_TYPE_DEV;
> + buf->dev.dev = pcm->card->dev;
> + buf->private_data = NULL;
> +
> + buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
> + &buf->addr, GFP_KERNEL);
> + if (!buf->area)
> + return -ENOMEM;
> +
> + buf->bytes = size;
> +
> + return 0;
> +}
> +
> +static void jz4740_pcm_free(struct snd_pcm *pcm)
> +{
> + struct snd_pcm_substream *substream;
> + struct snd_dma_buffer *buf;
> + int stream;
> +
> + for (stream = 0; stream < 2; stream++) {
> + substream = pcm->streams[stream].substream;
> + if (!substream)
> + continue;
> +
> + buf = &substream->dma_buffer;
> + if (!buf->area)
> + continue;
> +
> + dma_free_noncoherent(pcm->card->dev, buf->bytes,
> + buf->area, buf->addr);
> + buf->area = NULL;
> + }
> +}
> +
> +static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
> +
> +int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
> + struct snd_pcm *pcm)
> +{
> + int ret = 0;
> +
> + if (!card->dev->dma_mask)
> + card->dev->dma_mask = &jz4740_pcm_dmamask;
> +
> + if (!card->dev->coherent_dma_mask)
> + card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> + if (dai->playback.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_PLAYBACK);
> + if (ret)
> + goto err;
> + }
> +
> + if (dai->capture.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_CAPTURE);
> + if (ret)
> + goto err;
> + }
> +
> +err:
> + return ret;
> +}
> +
> +struct snd_soc_platform jz4740_soc_platform = {
> + .name = "jz4740-pcm",
> + .pcm_ops = &jz4740_pcm_ops,
> + .pcm_new = jz4740_pcm_new,
> + .pcm_free = jz4740_pcm_free,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_soc_platform);
> +
> +static int __init jz4740_soc_platform_init(void)
> +{
> + return snd_soc_register_platform(&jz4740_soc_platform);
> +}
> +module_init(jz4740_soc_platform_init);
> +
> +static void __exit jz4740_soc_platform_exit(void)
> +{
> + snd_soc_unregister_platform(&jz4740_soc_platform);
> +}
> +module_exit(jz4740_soc_platform_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
> +MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
> new file mode 100644
> index 0000000..e3f221e
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.h
> @@ -0,0 +1,22 @@
> +/*
> + *
> + * 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.
> + */
> +
> +#ifndef _JZ4740_PCM_H
> +#define _JZ4740_PCM_H
> +
> +#include <linux/dma-mapping.h>
> +#include <asm/mach-jz4740/dma.h>
> +
> +/* platform data */
> +extern struct snd_soc_platform jz4740_soc_platform;
> +
> +struct jz4740_pcm_config {
> + struct jz4740_dma_config dma_config;
> + phys_addr_t fifo_addr;
> +};
> +
> +#endif
Hi Lars-Peter Clausen,
Your all the patches have two kinds of 'WARNING:',as following:
(1)'WARNING: line over 80 characters' and
Please make a line less than 80 characters.
(2)'WARNING: EXPORT_SYMBOL(foo); should immediately follow its function/variable'
I can illustrate by a example,bellow:
struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
.probe = jz4740_codec_dev_probe,
.remove = jz4740_codec_dev_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
I think the ';' should be removed according to 'checkpatch.pl', but Andrew Morton just said the 'warning'
reason from the checkpatch script's own BUG, so far, I could not get any comments about this.
So, Mark & Liam, any comments regarding this 'checkpatch script's own BUG'?
Lars-Peter Clausen:
> This patch adds support for the JZ4740 internal codec.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Liam Girdwood <[email protected]>
> Cc: [email protected]
> ---
> sound/soc/codecs/Kconfig | 4 +
> sound/soc/codecs/Makefile | 2 +
> sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
> sound/soc/codecs/jz4740-codec.h | 20 ++
> 4 files changed, 528 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/codecs/jz4740-codec.c
> create mode 100644 sound/soc/codecs/jz4740-codec.h
>
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 31ac553..b8008df 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
> select SND_SOC_AK4671 if I2C
> select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
> select SND_SOC_CS4270 if I2C
> + select SND_SOC_JZ4740 if SOC_JZ4740
> select SND_SOC_MAX9877 if I2C
> select SND_SOC_DA7210 if I2C
> select SND_SOC_PCM3008
> @@ -269,6 +270,9 @@ config SND_SOC_WM9712
> config SND_SOC_WM9713
> tristate
>
> +config SND_SOC_JZ4740_CODEC
> + tristate
> +
> # Amp
> config SND_SOC_MAX9877
> tristate
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index 91429ea..4c7ee31 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
> snd-soc-wm9712-objs := wm9712.o
> snd-soc-wm9713-objs := wm9713.o
> snd-soc-wm-hubs-objs := wm_hubs.o
> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>
> # Amp
> snd-soc-max9877-objs := max9877.o
> @@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
> +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
>
> # Amp
> obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
> diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
> new file mode 100644
> index 0000000..6e4b741
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.c
> @@ -0,0 +1,502 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/delay.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/initval.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/soc.h>
> +
> +#define JZ4740_REG_CODEC_1 0x0
> +#define JZ4740_REG_CODEC_2 0x1
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
> +#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
> +#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
> +#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
> +#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
> +#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
> +#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
> +#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
> +#define JZ4740_CODEC_1_VREF_PULL_DOWN BIT(18)
> +#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
> +#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
> +#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
> +#define JZ4740_CODEC_1_HEADPHONE_CHANGE BIT(12)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_M BIT(11)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_R BIT(10)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M BIT(9)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN BIT(8)
> +#define JZ4740_CODEC_1_SUSPEND BIT(1)
> +#define JZ4740_CODEC_1_RESET BIT(0)
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
> +#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
> +#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
> +#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
> +#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
> +#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET 8
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
> +#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
> +#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
> +
> +struct jz4740_codec {
> + void __iomem *base;
> + struct resource *mem;
> +
> + uint32_t reg_cache[2];
> + struct snd_soc_codec codec;
> +};
> +
> +static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
> +{
> + return container_of(codec, struct jz4740_codec, codec);
> +}
> +
> +static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + return readl(jz4740_codec->base + (reg << 2));
> +}
> +
> +static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
> + unsigned int val)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + jz4740_codec->reg_cache[reg] = val;
> +
> + writel(val, jz4740_codec->base + (reg << 2));
> + return 0;
> +}
> +
> +static const struct snd_kcontrol_new jz4740_codec_controls[] = {
> + SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
> + SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
> + SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
> + SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
> + SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
> + SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
> + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
> + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
> +
> + SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
> + jz4740_codec_output_controls,
> + ARRAY_SIZE(jz4740_codec_output_controls)),
> +
> + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
> + jz4740_codec_input_controls,
> + ARRAY_SIZE(jz4740_codec_input_controls)),
> + SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
> +
> + SND_SOC_DAPM_OUTPUT("LOUT"),
> + SND_SOC_DAPM_OUTPUT("ROUT"),
> +
> + SND_SOC_DAPM_INPUT("MIC"),
> + SND_SOC_DAPM_INPUT("LIN"),
> + SND_SOC_DAPM_INPUT("RIN"),
> +};
> +
> +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
> +
> + {"Line Input", NULL, "LIN"},
> + {"Line Input", NULL, "RIN"},
> +
> + {"Input Mixer", "Line Capture Switch", "Line Input"},
> + {"Input Mixer", "Mic Capture Switch", "MIC"},
> +
> + {"ADC", NULL, "Input Mixer"},
> +
> + {"Output Mixer", "Bypass Switch", "Input Mixer"},
> + {"Output Mixer", "DAC Switch", "DAC"},
> +
> + {"LOUT", NULL, "Output Mixer"},
> + {"ROUT", NULL, "Output Mixer"},
> +};
> +
> +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + uint32_t val;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct snd_soc_device *socdev = rtd->socdev;
> + struct snd_soc_codec *codec = socdev->card->codec;
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + case SNDRV_PCM_FORMAT_S16_LE:
> + case SNDRV_PCM_FORMAT_S18_3LE:
> + break;
> + default:
> + return -EINVAL;
> + break;
> + }
> +
> + switch (params_rate(params)) {
> + case 8000:
> + val = 0;
> + break;
> + case 11025:
> + val = 1;
> + break;
> + case 12000:
> + val = 2;
> + break;
> + case 16000:
> + val = 3;
> + break;
> + case 22050:
> + val = 4;
> + break;
> + case 24000:
> + val = 5;
> + break;
> + case 32000:
> + val = 6;
> + break;
> + case 44100:
> + val = 7;
> + break;
> + case 48000:
> + val = 8;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
> + .hw_params = jz4740_codec_hw_params,
> + .set_fmt = jz4740_codec_set_fmt,
> +};
> +
> +struct snd_soc_dai jz4740_codec_dai = {
> + .name = "jz-codec",
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .ops = &jz4740_codec_dai_ops,
> + .symmetric_rates = 1,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_codec_dai);
> +
> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
> + enum snd_soc_bias_level level)
> +{
> +
> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
> + udelay(2);
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
> + }
> + switch (level) {
> + case SND_SOC_BIAS_ON:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE |
> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + 0);
> + break;
> + case SND_SOC_BIAS_PREPARE:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT);
> + break;
> + case SND_SOC_BIAS_STANDBY:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE);
> + break;
> + case SND_SOC_BIAS_OFF:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND, JZ4740_CODEC_1_SUSPEND);
> + break;
> + }
> + codec->bias_level = level;
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_codec *jz4740_codec_codec;
> +
> +static int jz4740_codec_dev_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + struct snd_soc_codec *codec = jz4740_codec_codec;
> +
> + BUG_ON(!codec);
> +
> + socdev->card->codec = codec;
> +
> + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
> + return ret;
> + }
> +
> + snd_soc_add_controls(codec, jz4740_codec_controls,
> + ARRAY_SIZE(jz4740_codec_controls));
> +
> + snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
> + ARRAY_SIZE(jz4740_codec_dapm_widgets));
> +
> + snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
> + ARRAY_SIZE(jz4740_codec_dapm_routes));
> +
> + snd_soc_dapm_new_widgets(codec);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_dev_remove(struct platform_device *pdev)
> +{
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + snd_soc_free_pcms(socdev);
> + snd_soc_dapm_free(socdev);
> +
> + return 0;
> +}
> +
> +struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
> + .probe = jz4740_codec_dev_probe,
> + .remove = jz4740_codec_dev_remove,
> +};
> +EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
> +
> +static int __devinit jz4740_codec_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct jz4740_codec *jz4740_codec;
> + struct snd_soc_codec *codec;
> +
> + jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
> +
> + if (!jz4740_codec)
> + return -ENOMEM;
> +
> + jz4740_codec->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
> + ret = -ENOENT;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->mem = request_mem_region(jz4740_codec->mem->start,
> + resource_size(jz4740_codec->mem), pdev->name);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to request mmio memory region\n");
> + ret = -EBUSY;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->base = ioremap(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + if (!jz4740_codec->base) {
> + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + jz4740_codec_dai.dev = &pdev->dev;
> +
> + codec = &jz4740_codec->codec;
> +
> + codec->dev = &pdev->dev;
> + codec->name = "jz-codec";
> + codec->owner = THIS_MODULE;
> +
> + codec->read = jz4740_codec_read;
> + codec->write = jz4740_codec_write;
> + codec->set_bias_level = jz4740_codec_set_bias_level;
> + codec->bias_level = SND_SOC_BIAS_OFF;
> +
> + codec->dai = &jz4740_codec_dai;
> + codec->num_dai = 1;
> +
> + codec->reg_cache = jz4740_codec->reg_cache;
> + codec->reg_cache_size = 2;
> +
> + mutex_init(&codec->mutex);
> + INIT_LIST_HEAD(&codec->dapm_widgets);
> + INIT_LIST_HEAD(&codec->dapm_paths);
> +
> + jz4740_codec_codec = codec;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
> +
> +
> + platform_set_drvdata(pdev, jz4740_codec);
> + ret = snd_soc_register_codec(codec);
> +
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec\n");
> + goto err_iounmap;
> + }
> +
> + ret = snd_soc_register_dai(&jz4740_codec_dai);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec dai\n");
> + goto err_unregister_codec;
> + }
> +
> + jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +
> + return 0;
> +err_unregister_codec:
> + snd_soc_unregister_codec(codec);
> +err_iounmap:
> + iounmap(jz4740_codec->base);
> +err_release_mem_region:
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +err_free_codec:
> + kfree(jz4740_codec);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_codec_remove(struct platform_device *pdev)
> +{
> + struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_codec_dai);
> + snd_soc_unregister_codec(&jz4740_codec->codec);
> +
> + iounmap(jz4740_codec->base);
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(jz4740_codec);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_codec_driver = {
> + .probe = jz4740_codec_probe,
> + .remove = __devexit_p(jz4740_codec_remove),
> + .driver = {
> + .name = "jz4740-codec",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_codec_init(void)
> +{
> + return platform_driver_register(&jz4740_codec_driver);
> +}
> +module_init(jz4740_codec_init);
> +
> +static void __exit jz4740_codec_exit(void)
> +{
> + platform_driver_unregister(&jz4740_codec_driver);
> +}
> +module_exit(jz4740_codec_exit);
> +
> +MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
> +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:jz-codec");
> diff --git a/sound/soc/codecs/jz4740-codec.h b/sound/soc/codecs/jz4740-codec.h
> new file mode 100644
> index 0000000..b5a0691
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright (C) 2009, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
> +#define __SND_SOC_CODECS_JZ4740_CODEC_H__
> +
> +extern struct snd_soc_dai jz4740_codec_dai;
> +extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
> +
> +#endif
On Thu, Jun 03, 2010 at 01:45:21PM +0800, Wan ZongShun wrote:
> Your all the patches have two kinds of 'WARNING:',as following:
> (1)'WARNING: line over 80 characters' and
> Please make a line less than 80 characters.
You need to apply a certain degree of taste when looking at checkpatch
warnings, particularly things like line length. Sometimes fixing the
warning for the sake of fixing the warning makes the code uglier than it
would be with whatever the style issue that's been identified is.
On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
> This patch adds support for the JZ4740 internal codec.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Liam Girdwood <[email protected]>
> Cc: [email protected]
> ---
> sound/soc/codecs/Kconfig | 4 +
> sound/soc/codecs/Makefile | 2 +
> sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
> sound/soc/codecs/jz4740-codec.h | 20 ++
> 4 files changed, 528 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/codecs/jz4740-codec.c
> create mode 100644 sound/soc/codecs/jz4740-codec.h
no need for code in file name here.
>
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 31ac553..b8008df 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
> select SND_SOC_AK4671 if I2C
> select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
> select SND_SOC_CS4270 if I2C
> + select SND_SOC_JZ4740 if SOC_JZ4740
> select SND_SOC_MAX9877 if I2C
> select SND_SOC_DA7210 if I2C
> select SND_SOC_PCM3008
> @@ -269,6 +270,9 @@ config SND_SOC_WM9712
> config SND_SOC_WM9713
> tristate
>
> +config SND_SOC_JZ4740_CODEC
> + tristate
> +
> # Amp
> config SND_SOC_MAX9877
> tristate
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index 91429ea..4c7ee31 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
> snd-soc-wm9712-objs := wm9712.o
> snd-soc-wm9713-objs := wm9713.o
> snd-soc-wm-hubs-objs := wm_hubs.o
> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>
Please use the same format here
> # Amp
> snd-soc-max9877-objs := max9877.o
> @@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
> +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
>
ditto.
> # Amp
> obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
> diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
> new file mode 100644
> index 0000000..6e4b741
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.c
> @@ -0,0 +1,502 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/delay.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/initval.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/soc.h>
> +
> +#define JZ4740_REG_CODEC_1 0x0
> +#define JZ4740_REG_CODEC_2 0x1
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
> +#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
> +#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
> +#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
> +#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
> +#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
> +#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
> +#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
> +#define JZ4740_CODEC_1_VREF_PULL_DOWN BIT(18)
> +#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
> +#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
> +#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
> +#define JZ4740_CODEC_1_HEADPHONE_CHANGE BIT(12)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_M BIT(11)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_R BIT(10)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M BIT(9)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN BIT(8)
> +#define JZ4740_CODEC_1_SUSPEND BIT(1)
> +#define JZ4740_CODEC_1_RESET BIT(0)
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
> +#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
> +#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
> +#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
> +#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
> +#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET 8
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
> +#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
> +#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
> +
> +struct jz4740_codec {
> + void __iomem *base;
> + struct resource *mem;
> +
> + uint32_t reg_cache[2];
> + struct snd_soc_codec codec;
> +};
> +
> +static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
> +{
> + return container_of(codec, struct jz4740_codec, codec);
> +}
> +
> +static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + return readl(jz4740_codec->base + (reg << 2));
> +}
> +
> +static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
> + unsigned int val)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + jz4740_codec->reg_cache[reg] = val;
> +
> + writel(val, jz4740_codec->base + (reg << 2));
> + return 0;
> +}
> +
> +static const struct snd_kcontrol_new jz4740_codec_controls[] = {
> + SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
> + SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
Is this the master capture volume ?
> + SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
> + SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
> + SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
> + SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
> + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
> + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
> +
> + SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
> + jz4740_codec_output_controls,
> + ARRAY_SIZE(jz4740_codec_output_controls)),
> +
> + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
> + jz4740_codec_input_controls,
> + ARRAY_SIZE(jz4740_codec_input_controls)),
> + SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
> +
> + SND_SOC_DAPM_OUTPUT("LOUT"),
> + SND_SOC_DAPM_OUTPUT("ROUT"),
> +
> + SND_SOC_DAPM_INPUT("MIC"),
> + SND_SOC_DAPM_INPUT("LIN"),
> + SND_SOC_DAPM_INPUT("RIN"),
> +};
> +
> +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
> +
> + {"Line Input", NULL, "LIN"},
> + {"Line Input", NULL, "RIN"},
> +
> + {"Input Mixer", "Line Capture Switch", "Line Input"},
> + {"Input Mixer", "Mic Capture Switch", "MIC"},
> +
> + {"ADC", NULL, "Input Mixer"},
> +
> + {"Output Mixer", "Bypass Switch", "Input Mixer"},
> + {"Output Mixer", "DAC Switch", "DAC"},
> +
> + {"LOUT", NULL, "Output Mixer"},
> + {"ROUT", NULL, "Output Mixer"},
> +};
> +
> +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + uint32_t val;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct snd_soc_device *socdev = rtd->socdev;
> + struct snd_soc_codec *codec = socdev->card->codec;
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + case SNDRV_PCM_FORMAT_S16_LE:
> + case SNDRV_PCM_FORMAT_S18_3LE:
> + break;
> + default:
> + return -EINVAL;
> + break;
> + }
The PCM format check is not required here as core checks this.
> +
> + switch (params_rate(params)) {
> + case 8000:
> + val = 0;
> + break;
> + case 11025:
> + val = 1;
> + break;
> + case 12000:
> + val = 2;
> + break;
> + case 16000:
> + val = 3;
> + break;
> + case 22050:
> + val = 4;
> + break;
> + case 24000:
> + val = 5;
> + break;
> + case 32000:
> + val = 6;
> + break;
> + case 44100:
> + val = 7;
> + break;
> + case 48000:
> + val = 8;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
> + .hw_params = jz4740_codec_hw_params,
> + .set_fmt = jz4740_codec_set_fmt,
> +};
> +
> +struct snd_soc_dai jz4740_codec_dai = {
> + .name = "jz-codec",
best to use jz4740 here
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .ops = &jz4740_codec_dai_ops,
> + .symmetric_rates = 1,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_codec_dai);
> +
> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
> + enum snd_soc_bias_level level)
> +{
> +
> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
> + udelay(2);
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
> + }
> + switch (level) {
> + case SND_SOC_BIAS_ON:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE |
> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + 0);
> + break;
> + case SND_SOC_BIAS_PREPARE:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT);
> + break;
> + case SND_SOC_BIAS_STANDBY:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE);
> + break;
> + case SND_SOC_BIAS_OFF:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND, JZ4740_CODEC_1_SUSPEND);
> + break;
> + }
> + codec->bias_level = level;
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_codec *jz4740_codec_codec;
> +
> +static int jz4740_codec_dev_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + struct snd_soc_codec *codec = jz4740_codec_codec;
> +
> + BUG_ON(!codec);
> +
> + socdev->card->codec = codec;
> +
> + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
> + return ret;
> + }
> +
> + snd_soc_add_controls(codec, jz4740_codec_controls,
> + ARRAY_SIZE(jz4740_codec_controls));
> +
> + snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
> + ARRAY_SIZE(jz4740_codec_dapm_widgets));
> +
> + snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
> + ARRAY_SIZE(jz4740_codec_dapm_routes));
> +
> + snd_soc_dapm_new_widgets(codec);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_dev_remove(struct platform_device *pdev)
> +{
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + snd_soc_free_pcms(socdev);
> + snd_soc_dapm_free(socdev);
> +
> + return 0;
> +}
> +
> +struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
> + .probe = jz4740_codec_dev_probe,
> + .remove = jz4740_codec_dev_remove,
> +};
> +EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
> +
> +static int __devinit jz4740_codec_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct jz4740_codec *jz4740_codec;
> + struct snd_soc_codec *codec;
> +
> + jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
> +
> + if (!jz4740_codec)
> + return -ENOMEM;
> +
> + jz4740_codec->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
> + ret = -ENOENT;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->mem = request_mem_region(jz4740_codec->mem->start,
> + resource_size(jz4740_codec->mem), pdev->name);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to request mmio memory region\n");
> + ret = -EBUSY;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->base = ioremap(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + if (!jz4740_codec->base) {
> + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + jz4740_codec_dai.dev = &pdev->dev;
> +
> + codec = &jz4740_codec->codec;
> +
> + codec->dev = &pdev->dev;
> + codec->name = "jz-codec";
> + codec->owner = THIS_MODULE;
> +
> + codec->read = jz4740_codec_read;
> + codec->write = jz4740_codec_write;
> + codec->set_bias_level = jz4740_codec_set_bias_level;
> + codec->bias_level = SND_SOC_BIAS_OFF;
> +
> + codec->dai = &jz4740_codec_dai;
> + codec->num_dai = 1;
> +
> + codec->reg_cache = jz4740_codec->reg_cache;
> + codec->reg_cache_size = 2;
> +
> + mutex_init(&codec->mutex);
> + INIT_LIST_HEAD(&codec->dapm_widgets);
> + INIT_LIST_HEAD(&codec->dapm_paths);
> +
> + jz4740_codec_codec = codec;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
> +
> +
> + platform_set_drvdata(pdev, jz4740_codec);
> + ret = snd_soc_register_codec(codec);
> +
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec\n");
> + goto err_iounmap;
> + }
> +
> + ret = snd_soc_register_dai(&jz4740_codec_dai);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec dai\n");
> + goto err_unregister_codec;
> + }
> +
> + jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +
> + return 0;
> +err_unregister_codec:
> + snd_soc_unregister_codec(codec);
> +err_iounmap:
> + iounmap(jz4740_codec->base);
> +err_release_mem_region:
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +err_free_codec:
> + kfree(jz4740_codec);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_codec_remove(struct platform_device *pdev)
> +{
> + struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_codec_dai);
> + snd_soc_unregister_codec(&jz4740_codec->codec);
> +
> + iounmap(jz4740_codec->base);
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(jz4740_codec);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_codec_driver = {
> + .probe = jz4740_codec_probe,
> + .remove = __devexit_p(jz4740_codec_remove),
> + .driver = {
> + .name = "jz4740-codec",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_codec_init(void)
> +{
> + return platform_driver_register(&jz4740_codec_driver);
> +}
> +module_init(jz4740_codec_init);
> +
> +static void __exit jz4740_codec_exit(void)
> +{
> + platform_driver_unregister(&jz4740_codec_driver);
> +}
> +module_exit(jz4740_codec_exit);
> +
> +MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
> +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:jz-codec");
> diff --git a/sound/soc/codecs/jz4740-codec.h b/sound/soc/codecs/jz4740-codec.h
> new file mode 100644
> index 0000000..b5a0691
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright (C) 2009, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
> +#define __SND_SOC_CODECS_JZ4740_CODEC_H__
> +
> +extern struct snd_soc_dai jz4740_codec_dai;
> +extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
> +
> +#endif
Thanks
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
> This patch adds ASoC support for JZ4740 SoCs I2S module.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Liam Girdwood <[email protected]>
> Cc: [email protected]
> ---
> sound/soc/Kconfig | 1 +
> sound/soc/Makefile | 1 +
> sound/soc/jz4740/Kconfig | 13 +
> sound/soc/jz4740/Makefile | 9 +
> sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-i2s.h | 18 ++
> sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-pcm.h | 22 ++
> 8 files changed, 982 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/jz4740/Kconfig
> create mode 100644 sound/soc/jz4740/Makefile
> create mode 100644 sound/soc/jz4740/jz4740-i2s.c
> create mode 100644 sound/soc/jz4740/jz4740-i2s.h
> create mode 100644 sound/soc/jz4740/jz4740-pcm.c
> create mode 100644 sound/soc/jz4740/jz4740-pcm.h
>
> diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
> index b1749bc..5a7a724 100644
> --- a/sound/soc/Kconfig
> +++ b/sound/soc/Kconfig
> @@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
> source "sound/soc/s6000/Kconfig"
> source "sound/soc/sh/Kconfig"
> source "sound/soc/txx9/Kconfig"
> +source "sound/soc/jz4740/Kconfig"
>
> # Supported codecs
> source "sound/soc/codecs/Kconfig"
> diff --git a/sound/soc/Makefile b/sound/soc/Makefile
> index 1470141..fdbe74d 100644
> --- a/sound/soc/Makefile
> +++ b/sound/soc/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
> obj-$(CONFIG_SND_SOC) += s6000/
> obj-$(CONFIG_SND_SOC) += sh/
> obj-$(CONFIG_SND_SOC) += txx9/
> +obj-$(CONFIG_SND_SOC) += jz4740/
> diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
> new file mode 100644
> index 0000000..39df949
> --- /dev/null
> +++ b/sound/soc/jz4740/Kconfig
> @@ -0,0 +1,13 @@
> +config SND_JZ4740_SOC
> + tristate "SoC Audio for Ingenic JZ4740 SoC"
> + depends on SOC_JZ4740 && SND_SOC
> + help
> + Say Y or M if you want to add support for codecs attached to
> + the Jz4740 AC97, I2S or SSP interface. You will also need
Do you have an AC97 or SSP interface ?
> + to select the audio interfaces to support below.
> +
> +config SND_JZ4740_SOC_I2S
> + depends on SND_JZ4740_SOC
> + tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
> + help
> + Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
> diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
> new file mode 100644
> index 0000000..1be8d19
> --- /dev/null
> +++ b/sound/soc/jz4740/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Jz4740 Platform Support
> +#
> +snd-soc-jz4740-objs := jz4740-pcm.o
> +snd-soc-jz4740-i2s-objs := jz4740-i2s.o
> +
> +obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
> +obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
> +
> diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
> new file mode 100644
> index 0000000..2b139fd
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.c
> @@ -0,0 +1,568 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
Formatting looks broken here.
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/initval.h>
> +
> +#include "jz4740-i2s.h"
> +#include "jz4740-pcm.h"
> +
> +#define JZ_REG_AIC_CONF 0x00
> +#define JZ_REG_AIC_CTRL 0x04
> +#define JZ_REG_AIC_I2S_FMT 0x10
> +#define JZ_REG_AIC_FIFO_STATUS 0x14
> +#define JZ_REG_AIC_I2S_STATUS 0x1c
> +#define JZ_REG_AIC_CLK_DIV 0x30
> +#define JZ_REG_AIC_FIFO 0x34
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
> +#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
> +#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
> +#define JZ_AIC_CONF_I2S BIT(4)
> +#define JZ_AIC_CONF_RESET BIT(3)
> +#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
> +#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
> +#define JZ_AIC_CONF_ENABLE BIT(0)
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
> +#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
> +#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
> +#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
> +#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
> +#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
> +#define JZ_AIC_CTRL_FLUSH BIT(8)
> +#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
> +#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
> +#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
> +#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
> +#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
> +#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
> +#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
> +
> +#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
> +#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
> +#define JZ_AIC_I2S_FMT_MSB BIT(0)
> +
> +#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
> +
> +#define JZ_AIC_CLK_DIV_MASK 0xf
> +
> +struct jz4740_i2s {
> + struct resource *mem;
> + void __iomem *base;
> + dma_addr_t phys_base;
> +
> + struct clk *clk_aic;
> + struct clk *clk_i2s;
> +
> + struct jz4740_pcm_config pcm_config_playback;
> + struct jz4740_pcm_config pcm_config_capture;
> +};
> +
> +static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
> + unsigned int reg)
> +{
> + return readl(i2s->base + reg);
> +}
> +
> +static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
> + unsigned int reg, uint32_t value)
> +{
> + writel(value, i2s->base + reg);
> +}
> +
> +static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
> +{
> + return dai->private_data;
> +}
> +
> +static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf, ctrl;
> +
> + if (dai->active)
> + return 0;
> +
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> + ctrl |= JZ_AIC_CTRL_FLUSH;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (!dai->active)
> + return;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> +}
> +
> +
> +static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +
> + uint32_t ctrl;
> + uint32_t mask;
> +
> + if (playback)
It's best to use (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) here.
> + mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
> + else
> + mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ctrl |= mask;
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + ctrl &= ~mask;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + return 0;
> +}
> +
> +
> +static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
No new line is required here.
> + uint32_t format = 0;
> + uint32_t conf;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> +
> + conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
> +
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBS_CFS:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
> + format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFS:
> + conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBS_CFM:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_MSB:
> + format |= JZ_AIC_I2S_FMT_MSB;
> + break;
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> + enum jz4740_dma_width dma_width;
> + struct jz4740_pcm_config *pcm_config;
> + unsigned int sample_size;
> + uint32_t ctrl;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + sample_size = 0;
> + dma_width = JZ4740_DMA_WIDTH_8BIT;
> + break;
> + case SNDRV_PCM_FORMAT_S16:
> + sample_size = 1;
> + dma_width = JZ4740_DMA_WIDTH_16BIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (playback) {
Same here re substream->stream
> + ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
> + } else {
> + ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
> + }
> +
> + switch (params_channels(params)) {
> + case 2:
> + break;
> + case 1:
> + if (playback) {
> + ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
> + break;
> + }
> + default: /* Falltrough */
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + if (playback) {
ditto
> + pcm_config = &i2s->pcm_config_playback;
> + pcm_config->dma_config.dst_width = dma_width;
> + } else {
> + pcm_config = &i2s->pcm_config_capture;
> + pcm_config->dma_config.src_width = dma_width;
> + }
> +
> +
> + snd_soc_dai_set_dma_data(dai, substream, pcm_config);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + switch (div_id) {
> + case JZ4740_I2S_BIT_CLK:
> + if (div & 1 || div > 16)
> + return -EINVAL;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + int ret = 0;
> + struct clk *parent;
> +
> + switch (clk_id) {
> + case JZ4740_I2S_CLKSRC_EXT:
> + parent = clk_get(NULL, "ext");
> + clk_set_parent(i2s->clk_i2s, parent);
> + break;
> + case JZ4740_I2S_CLKSRC_PLL:
> + parent = clk_get(NULL, "pll half");
> + clk_set_parent(i2s->clk_i2s, parent);
> + ret = clk_set_rate(i2s->clk_i2s, freq);
> + break;
> + default:
> + return -EINVAL;
> + }
> + clk_put(parent);
> +
> + return ret;
> +}
> +
> +static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (dai->active) {
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> + }
> +
> + clk_disable(i2s->clk_aic);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_resume(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + clk_enable(i2s->clk_aic);
> +
> + if (dai->active) {
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
> + (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
> + JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
> + JZ_AIC_CONF_I2S |
> + JZ_AIC_CONF_INTERNAL_CODEC;
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
> + .startup = jz4740_i2s_startup,
> + .shutdown = jz4740_i2s_shutdown,
> + .trigger = jz4740_i2s_trigger,
> + .hw_params = jz4740_i2s_hw_params,
> + .set_fmt = jz4740_i2s_set_fmt,
> + .set_clkdiv = jz4740_i2s_set_clkdiv,
> + .set_sysclk = jz4740_i2s_set_sysclk,
> +};
> +
> +#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
> + SNDRV_PCM_FMTBIT_S16_LE)
> +
> +struct snd_soc_dai jz4740_i2s_dai = {
> + .name = "jz4740-i2s",
> + .probe = jz4740_i2s_probe,
> + .playback = {
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .capture = {
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .symmetric_rates = 1,
> + .ops = &jz4740_i2s_dai_ops,
> + .suspend = jz4740_i2s_suspend,
> + .resume = jz4740_i2s_resume,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
> +
> +static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
> +{
> + struct jz4740_dma_config *dma_config;
> +
> + /* Playback */
> + dma_config = &i2s->pcm_config_playback.dma_config;
> + dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
> + dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +
> + /* Capture */
> + dma_config = &i2s->pcm_config_capture.dma_config;
> + dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
> + dma_config->flags = JZ4740_DMA_DST_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +}
> +
> +static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s;
> + int ret;
> +
> + i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
> +
> + if (!i2s)
> + return -ENOMEM;
> +
> + i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
Why the new lines here
> + if (!i2s->mem) {
> + ret = -ENOENT;
> + goto err_free;
> + }
> +
> + i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
> + pdev->name);
> +
and here
> + if (!i2s->mem) {
> + ret = -EBUSY;
> + goto err_free;
> + }
> +
> + i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
> +
ditto
> + if (!i2s->base) {
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + i2s->phys_base = i2s->mem->start;
> +
> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
> + if (IS_ERR(i2s->clk_aic)) {
> + ret = PTR_ERR(i2s->clk_aic);
> + goto err_iounmap;
> + }
> +
> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
> + if (IS_ERR(i2s->clk_i2s)) {
> + ret = PTR_ERR(i2s->clk_i2s);
> + goto err_iounmap;
> + }
> +
> + clk_enable(i2s->clk_aic);
> +
> + jz4740_i2c_init_pcm_config(i2s);
> +
> + jz4740_i2s_dai.private_data = i2s;
> + ret = snd_soc_register_dai(&jz4740_i2s_dai);
> +
> + platform_set_drvdata(pdev, i2s);
> +
> + return 0;
> +
> +err_iounmap:
> + iounmap(i2s->base);
> +err_release_mem_region:
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +err_free:
> + kfree(i2s);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_i2s_dai);
> +
> + clk_disable(i2s->clk_aic);
> + clk_put(i2s->clk_i2s);
> + clk_put(i2s->clk_aic);
> +
> + iounmap(i2s->base);
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(i2s);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_i2s_driver = {
> + .probe = jz4740_i2s_dev_probe,
> + .remove = __devexit_p(jz4740_i2s_dev_remove),
> + .driver = {
> + .name = "jz4740-i2s",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_i2s_init(void)
> +{
> + return platform_driver_register(&jz4740_i2s_driver);
> +}
> +module_init(jz4740_i2s_init);
> +
> +static void __exit jz4740_i2s_exit(void)
> +{
> + platform_driver_unregister(&jz4740_i2s_driver);
> +}
> +module_exit(jz4740_i2s_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen, <[email protected]>");
> +MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:jz4740-i2s");
> diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
> new file mode 100644
> index 0000000..da22ed8
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.h
> @@ -0,0 +1,18 @@
> +/*
> + * 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.
> + */
> +
> +#ifndef _JZ4740_I2S_H
> +#define _JZ4740_I2S_H
> +
> +/* I2S clock source */
> +#define JZ4740_I2S_CLKSRC_EXT 0
> +#define JZ4740_I2S_CLKSRC_PLL 1
> +
> +#define JZ4740_I2S_BIT_CLK 0
> +
> +extern struct snd_soc_dai jz4740_i2s_dai;
> +
> +#endif
> diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
> new file mode 100644
> index 0000000..fd1c203
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.c
> @@ -0,0 +1,350 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +
> +#include <asm/mach-jz4740/dma.h>
> +#include "jz4740-pcm.h"
> +
> +struct jz4740_runtime_data {
> + unsigned int dma_period;
> + dma_addr_t dma_start;
> + dma_addr_t dma_pos;
> + dma_addr_t dma_end;
> +
> + struct jz4740_dma_chan *dma;
> +
> + dma_addr_t fifo_addr;
> +};
> +
> +/* identify hardware playback capabilities */
> +static const struct snd_pcm_hardware jz4740_pcm_hardware = {
> + .info = SNDRV_PCM_INFO_MMAP |
> + SNDRV_PCM_INFO_MMAP_VALID |
> + SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_BLOCK_TRANSFER,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> +
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .channels_min = 1,
> + .channels_max = 2,
> + .period_bytes_min = 16,
> + .period_bytes_max = 2 * PAGE_SIZE,
> + .periods_min = 2,
> + .periods_max = 128,
> + .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
> + .fifo_size = 32,
> +};
> +
> +static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
> +{
> + unsigned int count;
> +
> + if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
> + count = prtd->dma_end - prtd->dma_pos;
> + else
> + count = prtd->dma_period;
> +
> + jz4740_dma_disable(prtd->dma);
> +
> + if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
> + } else {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
> + }
> +
> + jz4740_dma_set_transfer_count(prtd->dma, count);
> +
> + jz4740_dma_enable(prtd->dma);
> +
> + prtd->dma_pos += prtd->dma_period;
> + if (prtd->dma_pos >= prtd->dma_end)
> + prtd->dma_pos = prtd->dma_start;
> +}
> +
> +static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
> + void *dev_id)
> +{
> + struct snd_pcm_substream *substream = dev_id;
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + snd_pcm_period_elapsed(substream);
> +
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> +}
> +
> +static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct jz4740_pcm_config *config;
> +
> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
> + if (!prtd->dma) {
> + const char *dma_channel_name;
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> + dma_channel_name = "PCM Playback";
> + else
> + dma_channel_name = "PCM Capture";
> +
> + prtd->dma = jz4740_dma_request(substream, dma_channel_name);
dma_channel_name variable is not required here. Just use the const char
* directly.
> + }
> +
> + if (!prtd->dma)
> + return -EBUSY;
> +
> + jz4740_dma_configure(prtd->dma, &config->dma_config);
> + prtd->fifo_addr = config->fifo_addr;
> +
> + jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
> +
> + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> + runtime->dma_bytes = params_buffer_bytes(params);
> +
> + prtd->dma_period = params_period_bytes(params);
> + prtd->dma_start = runtime->dma_addr;
> + prtd->dma_pos = prtd->dma_start;
> + prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> +
> + snd_pcm_set_runtime_buffer(substream, NULL);
> + if (prtd->dma) {
> + jz4740_dma_free(prtd->dma);
> + prtd->dma = NULL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> + int ret = 0;
> +
> + if (!prtd->dma)
> + return 0;
> +
> + prtd->dma_pos = prtd->dma_start;
> +
> + return ret;
> +}
> +
> +static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
No new line.
> + int ret = 0;
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + jz4740_dma_disable(prtd->dma);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + unsigned long count, pos;
> + snd_pcm_uframes_t offset;
> + struct jz4740_dma_chan *dma = prtd->dma;
> +
> + count = jz4740_dma_get_residue(dma);
> + if (prtd->dma_pos == prtd->dma_start)
> + pos = prtd->dma_end - prtd->dma_start - count;
> + else
> + pos = prtd->dma_pos - prtd->dma_start - count;
> +
> + offset = bytes_to_frames(runtime, pos);
> + if (offset >= runtime->buffer_size)
> + offset = 0;
> +
Could you comment your calculation a little more.
> + return offset;
> +}
> +
> +static int jz4740_pcm_open(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd;
> +
> + snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
> + prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
> +
> + if (prtd == NULL)
> + return -ENOMEM;
> +
> + runtime->private_data = prtd;
> + return 0;
> +}
> +
> +static int jz4740_pcm_close(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + kfree(prtd);
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
> + struct vm_area_struct *vma)
> +{
> + return remap_pfn_range(vma, vma->vm_start,
> + substream->dma_buffer.addr >> PAGE_SHIFT,
> + vma->vm_end - vma->vm_start, vma->vm_page_prot);
> +}
> +
> +static struct snd_pcm_ops jz4740_pcm_ops = {
> + .open = jz4740_pcm_open,
> + .close = jz4740_pcm_close,
> + .ioctl = snd_pcm_lib_ioctl,
> + .hw_params = jz4740_pcm_hw_params,
> + .hw_free = jz4740_pcm_hw_free,
> + .prepare = jz4740_pcm_prepare,
> + .trigger = jz4740_pcm_trigger,
> + .pointer = jz4740_pcm_pointer,
> + .mmap = jz4740_pcm_mmap,
> +};
> +
> +static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
> +{
> + struct snd_pcm_substream *substream = pcm->streams[stream].substream;
> + struct snd_dma_buffer *buf = &substream->dma_buffer;
> + size_t size = jz4740_pcm_hardware.buffer_bytes_max;
> +
> + buf->dev.type = SNDRV_DMA_TYPE_DEV;
> + buf->dev.dev = pcm->card->dev;
> + buf->private_data = NULL;
> +
> + buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
> + &buf->addr, GFP_KERNEL);
> + if (!buf->area)
> + return -ENOMEM;
> +
> + buf->bytes = size;
> +
> + return 0;
> +}
> +
> +static void jz4740_pcm_free(struct snd_pcm *pcm)
> +{
> + struct snd_pcm_substream *substream;
> + struct snd_dma_buffer *buf;
> + int stream;
> +
> + for (stream = 0; stream < 2; stream++) {
> + substream = pcm->streams[stream].substream;
> + if (!substream)
> + continue;
> +
> + buf = &substream->dma_buffer;
> + if (!buf->area)
> + continue;
> +
> + dma_free_noncoherent(pcm->card->dev, buf->bytes,
> + buf->area, buf->addr);
> + buf->area = NULL;
> + }
> +}
> +
> +static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
> +
> +int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
> + struct snd_pcm *pcm)
> +{
> + int ret = 0;
> +
> + if (!card->dev->dma_mask)
> + card->dev->dma_mask = &jz4740_pcm_dmamask;
> +
> + if (!card->dev->coherent_dma_mask)
> + card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> + if (dai->playback.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_PLAYBACK);
> + if (ret)
> + goto err;
> + }
> +
> + if (dai->capture.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_CAPTURE);
> + if (ret)
> + goto err;
> + }
> +
> +err:
> + return ret;
> +}
> +
> +struct snd_soc_platform jz4740_soc_platform = {
> + .name = "jz4740-pcm",
> + .pcm_ops = &jz4740_pcm_ops,
> + .pcm_new = jz4740_pcm_new,
> + .pcm_free = jz4740_pcm_free,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_soc_platform);
> +
> +static int __init jz4740_soc_platform_init(void)
> +{
> + return snd_soc_register_platform(&jz4740_soc_platform);
> +}
> +module_init(jz4740_soc_platform_init);
> +
> +static void __exit jz4740_soc_platform_exit(void)
> +{
> + snd_soc_unregister_platform(&jz4740_soc_platform);
> +}
> +module_exit(jz4740_soc_platform_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
> +MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
> new file mode 100644
> index 0000000..e3f221e
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.h
> @@ -0,0 +1,22 @@
> +/*
> + *
> + * 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.
> + */
> +
> +#ifndef _JZ4740_PCM_H
> +#define _JZ4740_PCM_H
> +
> +#include <linux/dma-mapping.h>
> +#include <asm/mach-jz4740/dma.h>
> +
> +/* platform data */
> +extern struct snd_soc_platform jz4740_soc_platform;
> +
> +struct jz4740_pcm_config {
> + struct jz4740_dma_config dma_config;
> + phys_addr_t fifo_addr;
> +};
> +
> +#endif
Thanks
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
On Thu, 2010-06-03 at 13:32 +0100, Liam Girdwood wrote:
> On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
> > This patch adds support for the JZ4740 internal codec.
> >
> > Signed-off-by: Lars-Peter Clausen <[email protected]>
> > Cc: Mark Brown <[email protected]>
> > Cc: Liam Girdwood <[email protected]>
> > Cc: [email protected]
> > ---
> > sound/soc/codecs/Kconfig | 4 +
> > sound/soc/codecs/Makefile | 2 +
> > sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
> > sound/soc/codecs/jz4740-codec.h | 20 ++
> > 4 files changed, 528 insertions(+), 0 deletions(-)
> > create mode 100644 sound/soc/codecs/jz4740-codec.c
> > create mode 100644 sound/soc/codecs/jz4740-codec.h
>
> no need for code in file name here.
My bad typing - I mean codec.
Thanks
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
Hi Lars,
On Wednesday 02 June 2010 21:02:52 Lars-Peter Clausen wrote:
> This patch adds a new cpu type for the JZ4740 to the Linux MIPS
> architecture code. It also adds the iomem addresses for the different
> components found on a JZ4740 SoC.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> ---
[snip]
> * MIPS64 class processors
> diff --git a/arch/mips/include/asm/mach-jz4740/base.h
> b/arch/mips/include/asm/mach-jz4740/base.h new file mode 100644
> index 0000000..cba3aae
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-jz4740/base.h
> @@ -0,0 +1,28 @@
> +#ifndef __ASM_MACH_JZ4740_BASE_H__
> +#define __ASM_MACH_JZ4740_BASE_H__
> +
> +#define JZ4740_CPM_BASE_ADDR 0xb0000000
> +#define JZ4740_INTC_BASE_ADDR 0xb0001000
> +#define JZ4740_TCU_BASE_ADDR 0xb0002000
> +#define JZ4740_WDT_BASE_ADDR 0xb0002000
> +#define JZ4740_RTC_BASE_ADDR 0xb0003000
> +#define JZ4740_GPIO_BASE_ADDR 0xb0010000
> +#define JZ4740_AIC_BASE_ADDR 0xb0020000
> +#define JZ4740_ICDC_BASE_ADDR 0xb0020000
> +#define JZ4740_MSC_BASE_ADDR 0xb0021000
> +#define JZ4740_UART0_BASE_ADDR 0xb0030000
> +#define JZ4740_UART1_BASE_ADDR 0xb0031000
> +#define JZ4740_I2C_BASE_ADDR 0xb0042000
> +#define JZ4740_SSI_BASE_ADDR 0xb0043000
> +#define JZ4740_SADC_BASE_ADDR 0xb0070000
> +#define JZ4740_EMC_BASE_ADDR 0xb3010000
> +#define JZ4740_DMAC_BASE_ADDR 0xb3020000
> +#define JZ4740_UHC_BASE_ADDR 0xb3030000
> +#define JZ4740_UDC_BASE_ADDR 0xb3040000
> +#define JZ4740_LCD_BASE_ADDR 0xb3050000
> +#define JZ4740_SLCD_BASE_ADDR 0xb3050000
> +#define JZ4740_CIM_BASE_ADDR 0xb3060000
> +#define JZ4740_IPU_BASE_ADDR 0xb3080000
> +#define JZ4740_ETH_BASE_ADDR 0xb3100000
Any reasons why you prefered virtual addresses here instead of physical ones?
You might also want to define a "true" base address and compute the registers
offset relatively to this base address for better clarity.
> +
> +#endif
> diff --git a/arch/mips/include/asm/mach-jz4740/war.h
> b/arch/mips/include/asm/mach-jz4740/war.h new file mode 100644
> index 0000000..3a5bc17
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-jz4740/war.h
> @@ -0,0 +1,25 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General
> Public + * License. See the file "COPYING" in the main directory of this
> archive + * for more details.
> + *
> + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <[email protected]>
> + */
> +#ifndef __ASM_MIPS_MACH_JZ4740_WAR_H
> +#define __ASM_MIPS_MACH_JZ4740_WAR_H
> +
> +#define R4600_V1_INDEX_ICACHEOP_WAR 0
> +#define R4600_V1_HIT_CACHEOP_WAR 0
> +#define R4600_V2_HIT_CACHEOP_WAR 0
> +#define R5432_CP0_INTERRUPT_WAR 0
> +#define BCM1250_M3_WAR 0
> +#define SIBYTE_1956_WAR 0
> +#define MIPS4K_ICACHE_REFILL_WAR 0
> +#define MIPS_CACHE_SYNC_WAR 0
> +#define TX49XX_ICACHE_INDEX_INV_WAR 0
> +#define RM9000_CDEX_SMP_WAR 0
> +#define ICACHE_REFILLS_WORKAROUND_WAR 0
> +#define R10000_LLSC_WAR 0
> +#define MIPS34K_MISSED_ITLB_WAR 0
> +
> +#endif /* __ASM_MIPS_MACH_JZ4740_WAR_H */
> diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
> index 3562b85..9b66331 100644
> --- a/arch/mips/kernel/cpu-probe.c
> +++ b/arch/mips/kernel/cpu-probe.c
> @@ -187,6 +187,7 @@ void __init check_wait(void)
> case CPU_BCM6358:
> case CPU_CAVIUM_OCTEON:
> case CPU_CAVIUM_OCTEON_PLUS:
> + case CPU_JZRISC:
> cpu_wait = r4k_wait;
> break;
>
> @@ -956,6 +957,22 @@ platform:
> }
> }
>
> +static inline void cpu_probe_ingenic(struct cpuinfo_mips *c, unsigned int
> cpu) +{
> + decode_configs(c);
> + /* JZRISC does not implement the CP0 counter. */
> + c->options &= ~MIPS_CPU_COUNTER;
> + switch (c->processor_id & 0xff00) {
> + case PRID_IMP_JZRISC:
> + c->cputype = CPU_JZRISC;
> + __cpu_name[cpu] = "Ingenic JZRISC";
> + break;
> + default:
> + panic("Unknown Ingenic Processor ID!");
> + break;
> + }
> +}
> +
> const char *__cpu_name[NR_CPUS];
> const char *__elf_platform;
>
> @@ -994,6 +1011,9 @@ __cpuinit void cpu_probe(void)
> case PRID_COMP_CAVIUM:
> cpu_probe_cavium(c, cpu);
> break;
> + case PRID_COMP_INGENIC:
> + cpu_probe_ingenic(c, cpu);
> + break;
> }
>
> BUG_ON(!__cpu_name[cpu]);
> diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
> index 86f004d..4510e61 100644
> --- a/arch/mips/mm/tlbex.c
> +++ b/arch/mips/mm/tlbex.c
> @@ -409,6 +409,11 @@ static void __cpuinit build_tlb_write_entry(u32 **p,
> struct uasm_label **l, tlbw(p);
> break;
>
> + case CPU_JZRISC:
> + tlbw(p);
> + uasm_i_nop(p);
> + break;
> +
> default:
> panic("No TLB refill handler yet (CPU type: %d)",
> current_cpu_data.cputype);
On Wednesday 02 June 2010 21:02:53 Lars-Peter Clausen wrote:
> This patch adds support for IRQ handling on a JZ4740 SoC.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: Thomas Gleixner <[email protected]>
> ---
> arch/mips/include/asm/mach-jz4740/irq.h | 55 ++++++++++
> arch/mips/jz4740/irq.c | 170
> +++++++++++++++++++++++++++++++ arch/mips/jz4740/irq.h |
> 21 ++++
> 3 files changed, 246 insertions(+), 0 deletions(-)
> create mode 100644 arch/mips/include/asm/mach-jz4740/irq.h
> create mode 100644 arch/mips/jz4740/irq.c
> create mode 100644 arch/mips/jz4740/irq.h
>
> diff --git a/arch/mips/include/asm/mach-jz4740/irq.h
> b/arch/mips/include/asm/mach-jz4740/irq.h new file mode 100644
> index 0000000..5e27b78
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-jz4740/irq.h
> @@ -0,0 +1,55 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
> + * JZ7420/JZ4740 IRQ definitions
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> along + * with this program; if not, write to the Free Software
> Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#ifndef __ASM_MACH_JZ4740_IRQ_H__
> +#define __ASM_MACH_JZ4740_IRQ_H__
> +
> +#define MIPS_CPU_IRQ_BASE 0
> +#define JZ4740_IRQ_BASE 8
> +
> +/* 1st-level interrupts */
> +#define JZ4740_IRQ(x) (JZ4740_IRQ_BASE + (x))
> +#define JZ4740_IRQ_I2C JZ4740_IRQ(1)
> +#define JZ4740_IRQ_UHC JZ4740_IRQ(3)
> +#define JZ4740_IRQ_UART1 JZ4740_IRQ(8)
> +#define JZ4740_IRQ_UART0 JZ4740_IRQ(9)
> +#define JZ4740_IRQ_SADC JZ4740_IRQ(12)
> +#define JZ4740_IRQ_MSC JZ4740_IRQ(14)
> +#define JZ4740_IRQ_RTC JZ4740_IRQ(15)
> +#define JZ4740_IRQ_SSI JZ4740_IRQ(16)
> +#define JZ4740_IRQ_CIM JZ4740_IRQ(17)
> +#define JZ4740_IRQ_AIC JZ4740_IRQ(18)
> +#define JZ4740_IRQ_ETH JZ4740_IRQ(19)
> +#define JZ4740_IRQ_DMAC JZ4740_IRQ(20)
> +#define JZ4740_IRQ_TCU2 JZ4740_IRQ(21)
> +#define JZ4740_IRQ_TCU1 JZ4740_IRQ(22)
> +#define JZ4740_IRQ_TCU0 JZ4740_IRQ(23)
> +#define JZ4740_IRQ_UDC JZ4740_IRQ(24)
> +#define JZ4740_IRQ_GPIO3 JZ4740_IRQ(25)
> +#define JZ4740_IRQ_GPIO2 JZ4740_IRQ(26)
> +#define JZ4740_IRQ_GPIO1 JZ4740_IRQ(27)
> +#define JZ4740_IRQ_GPIO0 JZ4740_IRQ(28)
> +#define JZ4740_IRQ_IPU JZ4740_IRQ(29)
> +#define JZ4740_IRQ_LCD JZ4740_IRQ(30)
> +
> +/* 2nd-level interrupts */
> +#define JZ4740_IRQ_DMA(x) ((x) + JZ4740_IRQ(32))
> +
> +#define JZ4740_IRQ_INTC_GPIO(x) (JZ4740_IRQ_GPIO0 - (x))
> +#define JZ4740_IRQ_GPIO(x) (JZ4740_IRQ(48) + (x))
> +
> +#define NR_IRQS (JZ4740_IRQ_GPIO(127) + 1)
> +
> +#endif
> diff --git a/arch/mips/jz4740/irq.c b/arch/mips/jz4740/irq.c
> new file mode 100644
> index 0000000..46a03ee
> --- /dev/null
> +++ b/arch/mips/jz4740/irq.c
> @@ -0,0 +1,170 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
> + * JZ4740 platform IRQ support
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> along + * with this program; if not, write to the Free Software
> Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/timex.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +
> +#include <asm/io.h>
> +#include <asm/mipsregs.h>
> +#include <asm/irq_cpu.h>
> +
> +#include <asm/mach-jz4740/base.h>
> +
> +static void __iomem *jz_intc_base;
> +static uint32_t jz_intc_wakeup;
> +static uint32_t jz_intc_saved;
> +
> +#define JZ_REG_INTC_STATUS 0x00
> +#define JZ_REG_INTC_MASK 0x04
> +#define JZ_REG_INTC_SET_MASK 0x08
> +#define JZ_REG_INTC_CLEAR_MASK 0x0c
> +#define JZ_REG_INTC_PENDING 0x10
> +
> +#define IRQ_BIT(x) BIT((x) - JZ4740_IRQ_BASE)
> +
> +static void intc_irq_unmask(unsigned int irq)
> +{
> + writel(IRQ_BIT(irq), jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
> +}
> +
> +static void intc_irq_mask(unsigned int irq)
> +{
> + writel(IRQ_BIT(irq), jz_intc_base + JZ_REG_INTC_SET_MASK);
> +}
> +
> +static int intc_irq_set_wake(unsigned int irq, unsigned int on)
> +{
> + if (on)
> + jz_intc_wakeup |= IRQ_BIT(irq);
> + else
> + jz_intc_wakeup &= ~IRQ_BIT(irq);
> +
> + return 0;
> +}
> +
> +static struct irq_chip intc_irq_type = {
> + .name = "INTC",
> + .mask = intc_irq_mask,
> + .mask_ack = intc_irq_mask,
> + .unmask = intc_irq_unmask,
> + .set_wake = intc_irq_set_wake,
> +};
> +
> +static irqreturn_t jz4740_cascade(int irq, void *data)
> +{
> + uint32_t irq_reg;
> + int intc_irq;
> +
> + irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING);
> +
> + intc_irq = ffs(irq_reg);
> + if (intc_irq)
> + generic_handle_irq(intc_irq - 1 + JZ4740_IRQ_BASE);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static struct irqaction jz4740_cascade_action = {
> + .handler = jz4740_cascade,
> + .name = "JZ4740 cascade interrupt",
> + .flags = IRQF_DISABLED,
> +};
> +
> +void __init arch_init_irq(void)
> +{
> + int i;
> + mips_cpu_irq_init();
> +
> + jz_intc_base = ioremap(CPHYSADDR(JZ4740_INTC_BASE_ADDR), 0x14);
Better define the physical address and use ioremap directly on it.
> +
> + for (i = JZ4740_IRQ_BASE; i < JZ4740_IRQ_BASE + 32; i++) {
> + intc_irq_mask(i);
> + set_irq_chip_and_handler(i, &intc_irq_type, handle_level_irq);
> + }
> +
> + setup_irq(2, &jz4740_cascade_action);
> +}
> +
> +asmlinkage void plat_irq_dispatch(void)
> +{
> + unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
> + if (pending & STATUSF_IP2)
> + do_IRQ(2);
> + else if (pending & STATUSF_IP3)
> + do_IRQ(3);
> + else
> + spurious_interrupt();
> +}
> +
> +void jz4740_intc_suspend(void)
> +{
> + jz_intc_saved = readl(jz_intc_base + JZ_REG_INTC_MASK);
> + writel(~jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_SET_MASK);
> + writel(jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
> +}
> +
> +void jz4740_intc_resume(void)
> +{
> + writel(~jz_intc_saved, jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
> + writel(jz_intc_saved, jz_intc_base + JZ_REG_INTC_SET_MASK);
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static inline void intc_seq_reg(struct seq_file *s, const char *name,
> + unsigned int reg)
> +{
> + seq_printf(s, "%s:\t\t%08x\n", name, readl(jz_intc_base + reg));
> +}
> +
> +static int intc_regs_show(struct seq_file *s, void *unused)
> +{
> + intc_seq_reg(s, "Status", JZ_REG_INTC_STATUS);
> + intc_seq_reg(s, "Mask", JZ_REG_INTC_MASK);
> + intc_seq_reg(s, "Pending", JZ_REG_INTC_PENDING);
> +
> + return 0;
> +}
> +
> +static int intc_regs_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, intc_regs_show, NULL);
> +}
> +
> +static const struct file_operations intc_regs_operations = {
> + .open = intc_regs_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int __init intc_debugfs_init(void)
> +{
> + (void) debugfs_create_file("jz_regs_intc", S_IFREG | S_IRUGO,
> + NULL, NULL, &intc_regs_operations);
> + return 0;
> +}
> +subsys_initcall(intc_debugfs_init);
> +
> +#endif
> diff --git a/arch/mips/jz4740/irq.h b/arch/mips/jz4740/irq.h
> new file mode 100644
> index 0000000..dadbd5f
> --- /dev/null
> +++ b/arch/mips/jz4740/irq.h
> @@ -0,0 +1,21 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> along + * with this program; if not, write to the Free Software
> Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#ifndef __MIPS_JZ4740_IRQ_H__
> +#define __MIPS_JZ4740_IRQ_H__
> +
> +extern void jz4740_intc_suspend(void);
> +extern void jz4740_intc_resume(void);
> +
> +#endif
Liam Girdwood wrote:
> On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
>
>> This patch adds ASoC support for JZ4740 SoCs I2S module.
>>
>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>> Cc: Mark Brown <[email protected]>
>> Cc: Liam Girdwood <[email protected]>
>> Cc: [email protected]
>> ---
>> sound/soc/Kconfig | 1 +
>> sound/soc/Makefile | 1 +
>> sound/soc/jz4740/Kconfig | 13 +
>> sound/soc/jz4740/Makefile | 9 +
>> sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
>> sound/soc/jz4740/jz4740-i2s.h | 18 ++
>> sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
>> sound/soc/jz4740/jz4740-pcm.h | 22 ++
>> 8 files changed, 982 insertions(+), 0 deletions(-)
>> create mode 100644 sound/soc/jz4740/Kconfig
>> create mode 100644 sound/soc/jz4740/Makefile
>> create mode 100644 sound/soc/jz4740/jz4740-i2s.c
>> create mode 100644 sound/soc/jz4740/jz4740-i2s.h
>> create mode 100644 sound/soc/jz4740/jz4740-pcm.c
>> create mode 100644 sound/soc/jz4740/jz4740-pcm.h
>>
>> diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
>> index b1749bc..5a7a724 100644
>> --- a/sound/soc/Kconfig
>> +++ b/sound/soc/Kconfig
>> @@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
>> source "sound/soc/s6000/Kconfig"
>> source "sound/soc/sh/Kconfig"
>> source "sound/soc/txx9/Kconfig"
>> +source "sound/soc/jz4740/Kconfig"
>>
>> # Supported codecs
>> source "sound/soc/codecs/Kconfig"
>> diff --git a/sound/soc/Makefile b/sound/soc/Makefile
>> index 1470141..fdbe74d 100644
>> --- a/sound/soc/Makefile
>> +++ b/sound/soc/Makefile
>> @@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
>> obj-$(CONFIG_SND_SOC) += s6000/
>> obj-$(CONFIG_SND_SOC) += sh/
>> obj-$(CONFIG_SND_SOC) += txx9/
>> +obj-$(CONFIG_SND_SOC) += jz4740/
>> diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
>> new file mode 100644
>> index 0000000..39df949
>> --- /dev/null
>> +++ b/sound/soc/jz4740/Kconfig
>> @@ -0,0 +1,13 @@
>> +config SND_JZ4740_SOC
>> + tristate "SoC Audio for Ingenic JZ4740 SoC"
>> + depends on SOC_JZ4740 && SND_SOC
>> + help
>> + Say Y or M if you want to add support for codecs attached to
>> + the Jz4740 AC97, I2S or SSP interface. You will also need
>>
>
> Do you have an AC97 or SSP interface ?
>
>
Whoops. Copy-paste leftover...
>> + [....]
>> +
>> +
>> +static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
>> + struct snd_soc_dai *dai)
>> +{
>> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
>> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
>> +
>> + uint32_t ctrl;
>> + uint32_t mask;
>> +
>> + if (playback)
>>
>
> It's best to use (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) here.
>
>
hm, ok
>> + [...]
>> --- /dev/null
>> +++ b/sound/soc/jz4740/jz4740-pcm.c
>> @@ -0,0 +1,350 @@
>> + [...]
>> +static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
>> + struct snd_pcm_hw_params *params)
>> +{
>> + struct snd_pcm_runtime *runtime = substream->runtime;
>> + struct jz4740_runtime_data *prtd = runtime->private_data;
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct jz4740_pcm_config *config;
>> +
>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
>> + if (!prtd->dma) {
>> + const char *dma_channel_name;
>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> + dma_channel_name = "PCM Playback";
>> + else
>> + dma_channel_name = "PCM Capture";
>> +
>> + prtd->dma = jz4740_dma_request(substream, dma_channel_name);
>>
>
> dma_channel_name variable is not required here. Just use the const char
> * directly.
>
>
I actually had it like that before, but I think it is much more readable
in its current form. Considering that stream value will either be 0 or 1
it might work to put the channel names into a static array.
>> + [...]
>> +
>> +static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
>> +{
>> + struct snd_pcm_runtime *runtime = substream->runtime;
>> + struct jz4740_runtime_data *prtd = runtime->private_data;
>> + unsigned long count, pos;
>> + snd_pcm_uframes_t offset;
>> + struct jz4740_dma_chan *dma = prtd->dma;
>> +
>> + count = jz4740_dma_get_residue(dma);
>> + if (prtd->dma_pos == prtd->dma_start)
>> + pos = prtd->dma_end - prtd->dma_start - count;
>> + else
>> + pos = prtd->dma_pos - prtd->dma_start - count;
>> +
>> + offset = bytes_to_frames(runtime, pos);
>> + if (offset >= runtime->buffer_size)
>> + offset = 0;
>> +
>>
>
> Could you comment your calculation a little more.
>
Will do.
>
> Thanks
>
> Liam
>
Thanks for the review
- Lars
Liam Girdwood wrote:
> On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
>
>> This patch adds support for the JZ4740 internal codec.
>>
>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>> Cc: Mark Brown <[email protected]>
>> Cc: Liam Girdwood <[email protected]>
>> Cc: [email protected]
>> ---
>> sound/soc/codecs/Kconfig | 4 +
>> sound/soc/codecs/Makefile | 2 +
>> sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
>> sound/soc/codecs/jz4740-codec.h | 20 ++
>> 4 files changed, 528 insertions(+), 0 deletions(-)
>> create mode 100644 sound/soc/codecs/jz4740-codec.c
>> create mode 100644 sound/soc/codecs/jz4740-codec.h
>>
>
> no need for code in file name here.
>
>
>> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
>> index 31ac553..b8008df 100644
>> --- a/sound/soc/codecs/Kconfig
>> +++ b/sound/soc/codecs/Kconfig
>> @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
>> select SND_SOC_AK4671 if I2C
>> select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
>> select SND_SOC_CS4270 if I2C
>> + select SND_SOC_JZ4740 if SOC_JZ4740
>> select SND_SOC_MAX9877 if I2C
>> select SND_SOC_DA7210 if I2C
>> select SND_SOC_PCM3008
>> @@ -269,6 +270,9 @@ config SND_SOC_WM9712
>> config SND_SOC_WM9713
>> tristate
>>
>> +config SND_SOC_JZ4740_CODEC
>> + tristate
>> +
>> # Amp
>> config SND_SOC_MAX9877
>> tristate
>> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
>> index 91429ea..4c7ee31 100644
>> --- a/sound/soc/codecs/Makefile
>> +++ b/sound/soc/codecs/Makefile
>> @@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
>> snd-soc-wm9712-objs := wm9712.o
>> snd-soc-wm9713-objs := wm9713.o
>> snd-soc-wm-hubs-objs := wm_hubs.o
>> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>>
>>
>
> Please use the same format here
>
>
>> # Amp
>> snd-soc-max9877-objs := max9877.o
>> @@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
>> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
>> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
>> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
>> +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
>>
>>
>
> ditto.
>
Ok, I agree, but the Kconfig symbol should keep the "CODEC" in it,
otherwise it would clash with the JZ4740 ASoC platform support.
>
>> # Amp
>> obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
>> diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
>> new file mode 100644
>> index 0000000..6e4b741
>> --- /dev/null
>> +++ b/sound/soc/codecs/jz4740-codec.c
>> @@ -0,0 +1,502 @@
>> + [...]
>> +static const struct snd_kcontrol_new jz4740_codec_controls[] = {
>> + SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
>> + JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
>> + SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
>> + JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
>>
>
> Is this the master capture volume ?
>
Hm, yes.
>
>> + SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
>> + SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
>> + JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
>> +};
>> +
>> +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
>> + SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
>> + SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
>> +};
>> +
>> +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
>> + SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
>> + SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
>> + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
>> + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
>> +
>> + SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
>> + jz4740_codec_output_controls,
>> + ARRAY_SIZE(jz4740_codec_output_controls)),
>> +
>> + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
>> + jz4740_codec_input_controls,
>> + ARRAY_SIZE(jz4740_codec_input_controls)),
>> + SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
>> +
>> + SND_SOC_DAPM_OUTPUT("LOUT"),
>> + SND_SOC_DAPM_OUTPUT("ROUT"),
>> +
>> + SND_SOC_DAPM_INPUT("MIC"),
>> + SND_SOC_DAPM_INPUT("LIN"),
>> + SND_SOC_DAPM_INPUT("RIN"),
>> +};
>> +
>> +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
>> +
>> + {"Line Input", NULL, "LIN"},
>> + {"Line Input", NULL, "RIN"},
>> +
>> + {"Input Mixer", "Line Capture Switch", "Line Input"},
>> + {"Input Mixer", "Mic Capture Switch", "MIC"},
>> +
>> + {"ADC", NULL, "Input Mixer"},
>> +
>> + {"Output Mixer", "Bypass Switch", "Input Mixer"},
>> + {"Output Mixer", "DAC Switch", "DAC"},
>> +
>> + {"LOUT", NULL, "Output Mixer"},
>> + {"ROUT", NULL, "Output Mixer"},
>> +};
>> +
>> +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
>> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
>> +{
>> + uint32_t val;
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct snd_soc_device *socdev = rtd->socdev;
>> + struct snd_soc_codec *codec = socdev->card->codec;
>> +
>> + switch (params_format(params)) {
>> + case SNDRV_PCM_FORMAT_S8:
>> + case SNDRV_PCM_FORMAT_S16_LE:
>> + case SNDRV_PCM_FORMAT_S18_3LE:
>> + break;
>> + default:
>> + return -EINVAL;
>> + break;
>> + }
>>
>
> The PCM format check is not required here as core checks this.
>
Ok.
>> + [...]
>
> Thanks
>
> Liam
>
Thanks for reviewing
- Lars
On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
> >> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
> substream);
> >> + if (!prtd->dma) {
> >> + const char *dma_channel_name;
> >> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> >> + dma_channel_name = "PCM Playback";
> >> + else
> >> + dma_channel_name = "PCM Capture";
> >> +
> >> + prtd->dma = jz4740_dma_request(substream,
> dma_channel_name);
> >>
> >
> > dma_channel_name variable is not required here. Just use the const
> char
> > * directly.
> >
> >
> I actually had it like that before, but I think it is much more readable
> in its current form.
I disagree, having the char pointer here just adds an extra level of
indirection and costs an extra two lines of code.
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
Hi Florian
Florian Fainelli wrote:
> Hi Lars,
>
> On Wednesday 02 June 2010 21:02:52 Lars-Peter Clausen wrote:
>
>> This patch adds a new cpu type for the JZ4740 to the Linux MIPS
>> architecture code. It also adds the iomem addresses for the different
>> components found on a JZ4740 SoC.
>>
>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>> ---
>>
> [snip]
>
>
>> * MIPS64 class processors
>> diff --git a/arch/mips/include/asm/mach-jz4740/base.h
>> b/arch/mips/include/asm/mach-jz4740/base.h new file mode 100644
>> index 0000000..cba3aae
>> --- /dev/null
>> +++ b/arch/mips/include/asm/mach-jz4740/base.h
>> @@ -0,0 +1,28 @@
>> +#ifndef __ASM_MACH_JZ4740_BASE_H__
>> +#define __ASM_MACH_JZ4740_BASE_H__
>> +
>> +#define JZ4740_CPM_BASE_ADDR 0xb0000000
>> +#define JZ4740_INTC_BASE_ADDR 0xb0001000
>> +#define JZ4740_TCU_BASE_ADDR 0xb0002000
>> +#define JZ4740_WDT_BASE_ADDR 0xb0002000
>> +#define JZ4740_RTC_BASE_ADDR 0xb0003000
>> +#define JZ4740_GPIO_BASE_ADDR 0xb0010000
>> +#define JZ4740_AIC_BASE_ADDR 0xb0020000
>> +#define JZ4740_ICDC_BASE_ADDR 0xb0020000
>> +#define JZ4740_MSC_BASE_ADDR 0xb0021000
>> +#define JZ4740_UART0_BASE_ADDR 0xb0030000
>> +#define JZ4740_UART1_BASE_ADDR 0xb0031000
>> +#define JZ4740_I2C_BASE_ADDR 0xb0042000
>> +#define JZ4740_SSI_BASE_ADDR 0xb0043000
>> +#define JZ4740_SADC_BASE_ADDR 0xb0070000
>> +#define JZ4740_EMC_BASE_ADDR 0xb3010000
>> +#define JZ4740_DMAC_BASE_ADDR 0xb3020000
>> +#define JZ4740_UHC_BASE_ADDR 0xb3030000
>> +#define JZ4740_UDC_BASE_ADDR 0xb3040000
>> +#define JZ4740_LCD_BASE_ADDR 0xb3050000
>> +#define JZ4740_SLCD_BASE_ADDR 0xb3050000
>> +#define JZ4740_CIM_BASE_ADDR 0xb3060000
>> +#define JZ4740_IPU_BASE_ADDR 0xb3080000
>> +#define JZ4740_ETH_BASE_ADDR 0xb3100000
>>
>
> Any reasons why you prefered virtual addresses here instead of physical ones?
> You might also want to define a "true" base address and compute the registers
> offset relatively to this base address for better clarity.
>
>
This is historically grown and I agree that I should rather use the
physical addresses here, especially because they are only used together
with CPHYSADDR everywhere now.
Thanks for reviewing
- Lars
Liam Girdwood wrote:
> On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
>
>>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
>>>>
>> substream);
>>
>>>> + if (!prtd->dma) {
>>>> + const char *dma_channel_name;
>>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>>> + dma_channel_name = "PCM Playback";
>>>> + else
>>>> + dma_channel_name = "PCM Capture";
>>>> +
>>>> + prtd->dma = jz4740_dma_request(substream,
>>>>
>> dma_channel_name);
>>
>>>>
>>>>
>>> dma_channel_name variable is not required here. Just use the const
>>>
>> char
>>
>>> * directly.
>>>
>>>
>>>
>> I actually had it like that before, but I think it is much more readable
>> in its current form.
>>
>
> I disagree, having the char pointer here just adds an extra level of
> indirection and costs an extra two lines of code.
>
> Liam
>
Hi
Could you give an concrete example of how you would code it?
- Lars
Graham Gower wrote:
> On 3 June 2010 04:32, Lars-Peter Clausen <[email protected]> wrote:
>
>
>> + {
>> + .name = "dma",
>> + .parent = &jz_clk_high_speed_peripheral.clk,
>> + .gate_bit = JZ_CLOCK_GATE_UART0,
>> + .ops = &jz_clk_simple_ops,
>> + },
>>
>
> Presumably this should be JZ_CLOCK_GATE_DMAC.
>
> -Graham
>
Hi
Yes.
Thanks for reviewing
- Lars
On Thu, 2010-06-03 at 19:16 +0200, Lars-Peter Clausen wrote:
> Liam Girdwood wrote:
> > On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
> >
> >>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
> >>>>
> >> substream);
> >>
> >>>> + if (!prtd->dma) {
> >>>> + const char *dma_channel_name;
> >>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> >>>> + dma_channel_name = "PCM Playback";
> >>>> + else
> >>>> + dma_channel_name = "PCM Capture";
> >>>> +
> >>>> + prtd->dma = jz4740_dma_request(substream,
> >>>>
> >> dma_channel_name);
> >>
> >>>>
> >>>>
> >>> dma_channel_name variable is not required here. Just use the const
> >>>
> >> char
> >>
> >>> * directly.
> >>>
> >>>
> >>>
> >> I actually had it like that before, but I think it is much more readable
> >> in its current form.
> >>
> >
> > I disagree, having the char pointer here just adds an extra level of
> > indirection and costs an extra two lines of code.
> >
> > Liam
> >
> Hi
>
> Could you give an concrete example of how you would code it?
>
Sure,
if (!prtd->dma) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
prtd->dma = jz4740_dma_request(substream, "PCM Playback");
else
prtd->dma = jz4740_dma_request(substream, "PCM Capture");
}
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
Liam Girdwood wrote:
> On Thu, 2010-06-03 at 19:16 +0200, Lars-Peter Clausen wrote:
>
>> Liam Girdwood wrote:
>>
>>> On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
>>>
>>>
>>>>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
>>>>>>
>>>>>>
>>>> substream);
>>>>
>>>>
>>>>>> + if (!prtd->dma) {
>>>>>> + const char *dma_channel_name;
>>>>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>>>>> + dma_channel_name = "PCM Playback";
>>>>>> + else
>>>>>> + dma_channel_name = "PCM Capture";
>>>>>> +
>>>>>> + prtd->dma = jz4740_dma_request(substream,
>>>>>>
>>>>>>
>>>> dma_channel_name);
>>>>
>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>> dma_channel_name variable is not required here. Just use the const
>>>>>
>>>>>
>>>> char
>>>>
>>>>
>>>>> * directly.
>>>>>
>>>>>
>>>>>
>>>>>
>>>> I actually had it like that before, but I think it is much more readable
>>>> in its current form.
>>>>
>>>>
>>> I disagree, having the char pointer here just adds an extra level of
>>> indirection and costs an extra two lines of code.
>>>
>>> Liam
>>>
>>>
>> Hi
>>
>> Could you give an concrete example of how you would code it?
>>
>>
>
> Sure,
>
> if (!prtd->dma) {
> if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> prtd->dma = jz4740_dma_request(substream, "PCM Playback");
> else
> prtd->dma = jz4740_dma_request(substream, "PCM Capture");
> }
>
> Liam
>
>
And now you have the same statement in two different lines. When it
needs to be changed you have to change both lines.
And furthermore in my opinion it distracts from the reason for the if
statement: We want a different channel name.
But ok, if you insist on it, I can live with changing it.
- Lars
On Wed, Jun 02, 2010 at 09:12:26PM +0200, Lars-Peter Clausen wrote:
> This patch adds support for the JZ4740 internal codec.
This looks very good, there are some issues but nothing too major. I
may be repeating some things others have said but hopefully not too
much.
> snd-soc-wm9712-objs := wm9712.o
> snd-soc-wm9713-objs := wm9713.o
> snd-soc-wm-hubs-objs := wm_hubs.o
> +snd-soc-jz4740-codec-objs := jz4740-codec.o
Keep the devices sorted in both Makefile and Kconfig.
> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
This does nothing except validate some parameters. Is there actually an
externally visible DAI for this CODEC? If it's just integrated into the
SoC and there's nothing to configure then just omit the DAI
configuration since it's not even useful to document the signal format.
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
You listed an 18 bit format in hw_params - one or other of this and
hw_params is presumably out of date.
> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
> + enum snd_soc_bias_level level)
> +{
> +
> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
> + udelay(2);
I'd expect to see this as part of the _OFF in the main switch
statement.
> + switch (level) {
> + case SND_SOC_BIAS_ON:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE |
> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + 0);
This looks suspiciously like you should be using DAPM for the headphone
at least, though if there's only headphone out that's possibly not worth
it. Also, are you sure that you want both low and high current VREF
configuring here? I'm not clear what these settings do but the way
they're being managed both here and in _PREPARE seems odd.
> + codec = &jz4740_codec->codec;
> +
> + codec->dev = &pdev->dev;
> + codec->name = "jz-codec";
Seems a bit odd to use the part number in some places and not others.
On Wed, Jun 02, 2010 at 09:12:27PM +0200, Lars-Peter Clausen wrote:
Again, overall very good.
> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + switch (div_id) {
> + case JZ4740_I2S_BIT_CLK:
> + if (div & 1 || div > 16)
> + return -EINVAL;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
You can probably figure out the bit clock automatically by default...
> + if (dai->active) {
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> + }
> +
> + clk_disable(i2s->clk_aic);
Might make sense to manage this clock dynamically at runtime too for a
little extra power saving?
> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
> + if (IS_ERR(i2s->clk_aic)) {
> + ret = PTR_ERR(i2s->clk_aic);
> + goto err_iounmap;
> + }
> +
> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
> + if (IS_ERR(i2s->clk_i2s)) {
> + ret = PTR_ERR(i2s->clk_i2s);
> + goto err_iounmap;
> + }
Ideally you'd free the AIC clock when unwinding (and later stop it after
it was enabled). Though since you don't do any error checking after
this point it's kind of academic :)
On Wed, Jun 02, 2010 at 09:15:32PM +0200, Lars-Peter Clausen wrote:
> + ret = gpio_request(QI_LB60_SND_GPIO, "SND");
> + if (ret) {
> + pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
> + QI_LB60_SND_GPIO, ret);
> + goto err_device_put;
> + }
> +
> + ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
> + if (ret) {
> + pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
> + QI_LB60_AMP_GPIO, ret);
> + goto err_gpio_free_snd;
> + }
> +
> + gpio_direction_output(JZ_GPIO_PORTB(29), 0);
> + gpio_direction_output(JZ_GPIO_PORTD(4), 0);
You're referring to the GPIOs by multiple different names - it'd be more
robust to pick one way of naming them and use it consistently (probably
the #define).
Liam Girdwood wrote:
> On Thu, 2010-06-03 at 19:16 +0200, Lars-Peter Clausen wrote:
>> Liam Girdwood wrote:
>>> On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
>>>
>>>>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
>>>>>>
>>>> substream);
>>>>
>>>>>> + if (!prtd->dma) {
>>>>>> + const char *dma_channel_name;
>>>>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>>>>> + dma_channel_name = "PCM Playback";
>>>>>> + else
>>>>>> + dma_channel_name = "PCM Capture";
>>>>>> +
>>>>>> + prtd->dma = jz4740_dma_request(substream,
>>>>>>
>>>> dma_channel_name);
>>>>
>>>>>>
>>>>>>
>>>>> dma_channel_name variable is not required here. Just use the const
>>>>>
>>>> char
>>>>
>>>>> * directly.
>>>>>
>>>>>
>>>>>
>>>> I actually had it like that before, but I think it is much more readable
>>>> in its current form.
>>>>
>>> I disagree, having the char pointer here just adds an extra level of
>>> indirection and costs an extra two lines of code.
>>>
>>> Liam
>>>
>> Hi
>>
>> Could you give an concrete example of how you would code it?
>>
>
> Sure,
>
> if (!prtd->dma) {
> if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> prtd->dma = jz4740_dma_request(substream, "PCM Playback");
> else
> prtd->dma = jz4740_dma_request(substream, "PCM Capture");
> }
>
> Liam
>
or,
if (!prtd->dma)
prtd->dma = jz4740_dma_request(substream, (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
"PCM Playback" : "PCM Capture");
Mark Brown wrote:
> On Wed, Jun 02, 2010 at 09:12:27PM +0200, Lars-Peter Clausen wrote:
>
> Again, overall very good.
>
>
>> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
>> +{
>> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
>> +
>> + switch (div_id) {
>> + case JZ4740_I2S_BIT_CLK:
>> + if (div & 1 || div > 16)
>> + return -EINVAL;
>> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>>
>
> You can probably figure out the bit clock automatically by default...
>
Hm, yes, you are right.
>
>> + if (dai->active) {
>> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
>> + conf &= ~JZ_AIC_CONF_ENABLE;
>> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
>> +
>> + clk_disable(i2s->clk_i2s);
>> + }
>> +
>> + clk_disable(i2s->clk_aic);
>>
>
> Might make sense to manage this clock dynamically at runtime too for a
> little extra power saving?
>
I think I tried it once and the power savings were marginal. On the
other hand each callback would have to make sure the clock is enabled
before accessing registers and disabling it again if it was disabled.
>
>> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
>> + if (IS_ERR(i2s->clk_aic)) {
>> + ret = PTR_ERR(i2s->clk_aic);
>> + goto err_iounmap;
>> + }
>> +
>> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
>> + if (IS_ERR(i2s->clk_i2s)) {
>> + ret = PTR_ERR(i2s->clk_i2s);
>> + goto err_iounmap;
>> + }
>>
>
> Ideally you'd free the AIC clock when unwinding (and later stop it after
> it was enabled). Though since you don't do any error checking after
> this point it's kind of academic :)
>
>
It should at least freed if the i2s clock is not found.
- Lars
Hi
Mark Brown wrote:
> On Wed, Jun 02, 2010 at 09:12:26PM +0200, Lars-Peter Clausen wrote:
>
>
>> This patch adds support for the JZ4740 internal codec.
>>
>
> This looks very good, there are some issues but nothing too major. I
> may be repeating some things others have said but hopefully not too
> much.
>
>
>> snd-soc-wm9712-objs := wm9712.o
>> snd-soc-wm9713-objs := wm9713.o
>> snd-soc-wm-hubs-objs := wm_hubs.o
>> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>>
>
> Keep the devices sorted in both Makefile and Kconfig.
>
>
>> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
>> +{
>> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
>> + case SND_SOC_DAIFMT_CBM_CFM:
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>>
>
> This does nothing except validate some parameters. Is there actually an
> externally visible DAI for this CODEC? If it's just integrated into the
> SoC and there's nothing to configure then just omit the DAI
> configuration since it's not even useful to document the signal format.
>
Nope, there is no externally visible DAI for the codec, but internally
it is connected through the i2s controller of the JZ4740 which supports
different operating modes. And if you'll do
"snd_soc_dai_set_fmt(codec_dai, BOARD_DAIFMT);
snd_soc_dai_set_fmt(cpu_dai, BOARD_DAIFMT);" with a wrong dai format it
will be noticed.
>> + .capture = {
>> + .stream_name = "Capture",
>> + .channels_min = 2,
>> + .channels_max = 2,
>> + .rates = SNDRV_PCM_RATE_8000_48000,
>> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
>>
>
> You listed an 18 bit format in hw_params - one or other of this and
> hw_params is presumably out of date.
>
In theory the codec supports 18 bit for playback, but the i2s controller
requires it to be 32 bit aligned, while alsa appears to have only
support for a 24 bit aligned 18 bit format(Correct me if I'm wrong). So
I dropped it, I'll remove the whole format check in hw_params as Liam
suggested.
>> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
>> + enum snd_soc_bias_level level)
>> +{
>> +
>> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
>> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
>> + udelay(2);
>>
>
> I'd expect to see this as part of the _OFF in the main switch
> statement.
>
Uhm, actually this code path is taken when switching from _OFF to
another state. If it is guaranteed that _OFF is always followed by a
certain other state I could put the reset code in its part of the switch
statement.
>
>> + switch (level) {
>> + case SND_SOC_BIAS_ON:
>> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_VREF_DISABLE |
>> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
>> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
>> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
>> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
>> + 0);
>>
>
> This looks suspiciously like you should be using DAPM for the headphone
> at least, though if there's only headphone out that's possibly not worth
> it. Also, are you sure that you want both low and high current VREF
> configuring here? I'm not clear what these settings do but the way
> they're being managed both here and in _PREPARE seems odd.
>
Hm, I'll take a look.
>
>> + codec = &jz4740_codec->codec;
>> +
>> + codec->dev = &pdev->dev;
>> + codec->name = "jz-codec";
>>
>
> Seems a bit odd to use the part number in some places and not others.
>
I renamed the driver from jz-codec to jz4740-codec shortly before
submitting it after I realized that the codec component on other jz47xx
chips is completely different. Seems like I missed the codec name.
Thanks for reviewing the patch
- Lars
On 4 Jun 2010, at 00:57, Lars-Peter Clausen wrote:
> Mark Brown wrote:
> Uhm, actually this code path is taken when switching from _OFF to
> another state. If it is guaranteed that _OFF is always followed by a
> certain other state I could put the reset code in its part of the switch
> statement.
The only possible transitions are OFF <-> STANDBY <-> PREPARE <-> ON
On Wed, Jun 02, 2010 at 09:10:30PM +0200, Lars-Peter Clausen wrote:
> This patch adds the Kbuild files for the JZ4740 architecture and adds JZ4740
> support to the MIPS Kbuild files.
Checkout the changes to the way MIPS platform Makefiles are implemented in
the linux-queue tree. This new structure will be mandatory for all new
platforms.
Ralf
2010/6/3 Lars-Peter Clausen <[email protected]>:
> This patch adds support for the RTC unit on JZ4740 SoCs.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: Alessandro Zummo <[email protected]>
> Cc: Paul Gortmaker <[email protected]>
> Cc: [email protected]
> ---
> drivers/rtc/Kconfig | 11 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-jz4740.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 356 insertions(+), 0 deletions(-)
> create mode 100644 drivers/rtc/rtc-jz4740.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 10ba12c..d0ed7e6 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -905,4 +905,15 @@ config RTC_DRV_MPC5121
> This driver can also be built as a module. If so, the module
> will be called rtc-mpc5121.
>
> +config RTC_DRV_JZ4740
> + tristate "Ingenic JZ4740 SoC"
> + depends on RTC_CLASS
> + depends on MACH_JZ4740
> + help
> + If you say yes here you get support for the Ingenic JZ4740 SoC RTC
> + controller.
> +
> + This driver can also be buillt as a module. If so, the module
> + will be called rtc-jz4740.
> +
> endif # RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 5adbba7..fedf9bb 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -47,6 +47,7 @@ obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
> obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
> obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
> obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
> +obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
> obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
> obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
> obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
> diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
> new file mode 100644
> index 0000000..41ab78f
> --- /dev/null
> +++ b/drivers/rtc/rtc-jz4740.c
> @@ -0,0 +1,344 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
> + * JZ4740 SoC RTC driver
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#define JZ_REG_RTC_CTRL 0x00
> +#define JZ_REG_RTC_SEC 0x04
> +#define JZ_REG_RTC_SEC_ALARM 0x08
> +#define JZ_REG_RTC_REGULATOR 0x0C
> +#define JZ_REG_RTC_HIBERNATE 0x20
> +#define JZ_REG_RTC_SCRATCHPAD 0x34
> +
> +#define JZ_RTC_CTRL_WRDY BIT(7)
> +#define JZ_RTC_CTRL_1HZ BIT(6)
> +#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
> +#define JZ_RTC_CTRL_AF BIT(4)
> +#define JZ_RTC_CTRL_AF_IRQ BIT(3)
> +#define JZ_RTC_CTRL_AE BIT(2)
> +#define JZ_RTC_CTRL_ENABLE BIT(0)
> +
> +struct jz4740_rtc {
> + struct resource *mem;
> + void __iomem *base;
> +
> + struct rtc_device *rtc;
> +
> + unsigned int irq;
> +
> + spinlock_t lock;
> +};
> +
> +static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
> +{
> + return readl(rtc->base + reg);
> +}
> +
> +static inline void jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
> +{
> + uint32_t ctrl;
> + do {
> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
> + } while (!(ctrl & JZ_RTC_CTRL_WRDY));
> +}
> +
> +
> +static inline void jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
> + uint32_t val)
> +{
> + jz4740_rtc_wait_write_ready(rtc);
> + writel(val, rtc->base + reg);
> +}
> +
> +static void jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask,
> + uint32_t val)
> +{
> + unsigned long flags;
> + uint32_t ctrl;
> +
> + spin_lock_irqsave(&rtc->lock, flags);
> +
> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
> +
> + /* Don't clear interrupt flags by accident */
> + ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF;
> +
> + ctrl &= ~mask;
> + ctrl |= val;
> +
> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl);
> +
> + spin_unlock_irqrestore(&rtc->lock, flags);
> +}
> +
> +static inline struct jz4740_rtc *dev_to_rtc(struct device *dev)
> +{
> + return dev_get_drvdata(dev);
> +}
> +
Why do you need to re-implement the 'dev_to_rtc' instead of using
'platform_get_drvdata' provided by Linux own.'
> +static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time)
> +{
> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
> + uint32_t secs, secs2;
> +
> + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
> +
> + while (secs != secs2) {
> + secs = secs2;
> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
> + }
> +
> + rtc_time_to_tm(secs, time);
> +
> + return rtc_valid_tm(time);
> +}
> +
> +static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs)
> +{
> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
> +
> + if ((uint32_t)secs != secs)
> + return -EINVAL;
> +
> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs);
> +
> + return 0;
> +}
> +
> +static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
> + uint32_t secs, secs2;
> + uint32_t ctrl;
> +
> + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
> +
> + while (secs != secs2) {
> + secs = secs2;
> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
> + }
> +
> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
> +
> + alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE);
> + alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF);
> +
> + rtc_time_to_tm(secs, &alrm->time);
> +
> + return rtc_valid_tm(&alrm->time);
> +}
> +
> +static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
> + unsigned long secs;
> +
> + rtc_tm_to_time(&alrm->time, &secs);
> +
> + if ((uint32_t)secs != secs)
> + return -EINVAL;
> +
> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, (uint32_t)secs);
> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE,
> + alrm->enabled ? JZ_RTC_CTRL_AE : 0);
> +
> + return 0;
> +}
> +
> +static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enable)
> +{
> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ,
> + enable ? JZ_RTC_CTRL_1HZ_IRQ : 0);
> + return 0;
> +}
> +
> +
> +static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
> +{
> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ,
> + enable ? JZ_RTC_CTRL_AF_IRQ : 0);
> + return 0;
> +}
> +
> +static struct rtc_class_ops jz4740_rtc_ops = {
> + .read_time = jz4740_rtc_read_time,
> + .set_mmss = jz4740_rtc_set_mmss,
> + .read_alarm = jz4740_rtc_read_alarm,
> + .set_alarm = jz4740_rtc_set_alarm,
> + .update_irq_enable = jz4740_rtc_update_irq_enable,
> + .alarm_irq_enable = jz4740_rtc_alarm_irq_enable,
> +};
> +
> +static irqreturn_t jz4740_rtc_irq(int irq, void *data)
> +{
> + struct jz4740_rtc *rtc = data;
> + uint32_t ctrl;
> + unsigned long events = 0;
> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
> +
> + if (ctrl & JZ_RTC_CTRL_1HZ)
> + events |= (RTC_UF | RTC_IRQF);
> +
> + if (ctrl & JZ_RTC_CTRL_AF)
> + events |= (RTC_AF | RTC_IRQF);
> +
> + rtc_update_irq(rtc->rtc, 1, events);
> +
> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, 0);
> +
> + return IRQ_HANDLED;
> +}
> +
> +void jz4740_rtc_poweroff(struct device *dev)
> +{
> + struct jz4740_rtc *rtc = dev_get_drvdata(dev);
> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1);
> +}
> +EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff);
> +
> +static int __devinit jz4740_rtc_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct jz4740_rtc *rtc;
> + uint32_t scratchpad;
> +
> + rtc = kmalloc(sizeof(*rtc), GFP_KERNEL);
> +
> + rtc->irq = platform_get_irq(pdev, 0);
> +
> + if (rtc->irq < 0) {
> + ret = -ENOENT;
> + dev_err(&pdev->dev, "Failed to get platform irq\n");
> + goto err_free;
> + }
> +
> + rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!rtc->mem) {
> + ret = -ENOENT;
> + dev_err(&pdev->dev, "Failed to get platform mmio memory\n");
> + goto err_free;
> + }
> +
> + rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem),
> + pdev->name);
> +
> + if (!rtc->mem) {
> + ret = -EBUSY;
> + dev_err(&pdev->dev, "Failed to request mmio memory region\n");
> + goto err_free;
> + }
> +
> + rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem));
> +
> + if (!rtc->base) {
> + ret = -EBUSY;
> + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
> + goto err_release_mem_region;
> + }
> +
> + spin_lock_init(&rtc->lock);
> +
> + platform_set_drvdata(pdev, rtc);
> +
> + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops,
> + THIS_MODULE);
> +
> + if (IS_ERR(rtc->rtc)) {
> + ret = PTR_ERR(rtc->rtc);
> + dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret);
> + goto err_iounmap;
> + }
> +
> + ret = request_irq(rtc->irq, jz4740_rtc_irq, 0,
> + pdev->name, rtc);
> +
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret);
> + goto err_unregister_rtc;
> + }
> +
> + scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD);
> + if (scratchpad != 0x12345678) {
> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
> + }
> +
> + return 0;
> +
> +err_unregister_rtc:
> + rtc_device_unregister(rtc->rtc);
> +err_iounmap:
> + platform_set_drvdata(pdev, NULL);
> + iounmap(rtc->base);
> +err_release_mem_region:
> + release_mem_region(rtc->mem->start, resource_size(rtc->mem));
> +err_free:
> + kfree(rtc);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_rtc_remove(struct platform_device *pdev)
> +{
> + struct jz4740_rtc *rtc = platform_get_drvdata(pdev);
> +
> + free_irq(rtc->irq, rtc);
> +
> + rtc_device_unregister(rtc->rtc);
> +
> + iounmap(rtc->base);
> + release_mem_region(rtc->mem->start, resource_size(rtc->mem));
> +
> + kfree(rtc);
> +
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +struct platform_driver jz4740_rtc_driver = {
> + .probe = jz4740_rtc_probe,
> + .remove = __devexit_p(jz4740_rtc_remove),
> + .driver = {
> + .name = "jz4740-rtc",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_rtc_init(void)
> +{
> + return platform_driver_register(&jz4740_rtc_driver);
platform_driver_probe is much better here.
> +}
> +module_init(jz4740_rtc_init);
> +
> +static void __exit jz4740_rtc_exit(void)
> +{
> + platform_driver_unregister(&jz4740_rtc_driver);
> +}
> +module_exit(jz4740_rtc_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n");
> +MODULE_ALIAS("platform:jz4740-rtc");
> --
> 1.5.6.5
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
--
*linux-arm-kernel mailing list
mail addr:[email protected]
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
* linux-arm-NUC900 mailing list
mail addr:[email protected]
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
[email protected]
On 06/02/10 20:12, Lars-Peter Clausen wrote:
> This patch adds support for the ADC module on JZ4740 SoCs.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: [email protected]
> ---
> drivers/hwmon/Kconfig | 11 ++
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/jz4740-adc.c | 423 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/jz4740-adc.h | 25 +++
> 4 files changed, 460 insertions(+), 0 deletions(-)
> create mode 100644 drivers/hwmon/jz4740-adc.c
> create mode 100644 include/linux/jz4740-adc.h
Hi, I'm just wondering of one wants the majority of this driver to sit in hwmon?
Looks to me like a fairly classic case for something that might be best implemented
as an mfd with the hwmon, touchscreen and battery drivers separately hanging off that.
You might well have someone who needs the battery driver to work, but doesn't care
about hwmon and so doesn't want to build that bit in...
Just an immediate thought. Perhaps this is the best way to do things...
Also after a quick look. How is it used by the touchscreen driver?
If not, please remove the reference from kconfig until it it is true.
Few other bits and bobs inline.
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index e19cf8e..da79ba9 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -446,6 +446,17 @@ config SENSORS_IT87
> This driver can also be built as a module. If so, the module
> will be called it87.
>
> +config SENSORS_JZ4740
> + tristate "Ingenic JZ4740 SoC ADC driver"
> + depends on MACH_JZ4740
> + help
> + If you say yes here you get support for the Ingenic JZ4740 SoC ADC core.
> + It is required for the JZ4740 battery and touchscreen driver and is used
> + to synchronize access to the adc module between those two.
> +
> + This driver can also be build as a module. If so, the module will be
> + called jz4740-adc.
> +
> config SENSORS_LM63
> tristate "National Semiconductor LM63 and LM64"
> depends on I2C
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 2138ceb..3e772aa 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
> obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
> obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
> obj-$(CONFIG_SENSORS_IT87) += it87.o
> +obj-$(CONFIG_SENSORS_JZ4740) += jz4740-adc.o
> obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
> obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
> obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
> diff --git a/drivers/hwmon/jz4740-adc.c b/drivers/hwmon/jz4740-adc.c
> new file mode 100644
> index 0000000..635dfe9
> --- /dev/null
> +++ b/drivers/hwmon/jz4740-adc.c
> @@ -0,0 +1,423 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
> + * JZ4740 SoC ADC driver
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * This driver is meant to synchronize access to the adc core for the battery
> + * and touchscreen driver. Thus these drivers should use the adc driver as a
> + * parent.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +
> +#include <linux/clk.h>
> +
> +#include <linux/jz4740-adc.h>
> +
> +#define JZ_REG_ADC_ENABLE 0x00
> +#define JZ_REG_ADC_CFG 0x04
> +#define JZ_REG_ADC_CTRL 0x08
> +#define JZ_REG_ADC_STATUS 0x0C
> +#define JZ_REG_ADC_SAME 0x10
> +#define JZ_REG_ADC_WAIT 0x14
> +#define JZ_REG_ADC_TOUCH 0x18
> +#define JZ_REG_ADC_BATTERY 0x1C
> +#define JZ_REG_ADC_ADCIN 0x20
> +
> +#define JZ_ADC_ENABLE_TOUCH BIT(2)
> +#define JZ_ADC_ENABLE_BATTERY BIT(1)
> +#define JZ_ADC_ENABLE_ADCIN BIT(0)
> +
> +#define JZ_ADC_CFG_SPZZ BIT(31)
> +#define JZ_ADC_CFG_EX_IN BIT(30)
> +#define JZ_ADC_CFG_DNUM_MASK (0x7 << 16)
> +#define JZ_ADC_CFG_DMA_ENABLE BIT(15)
> +#define JZ_ADC_CFG_XYZ_MASK (0x2 << 13)
> +#define JZ_ADC_CFG_SAMPLE_NUM_MASK (0x7 << 10)
> +#define JZ_ADC_CFG_CLKDIV (0xf << 5)
> +#define JZ_ADC_CFG_BAT_MB BIT(4)
> +
> +#define JZ_ADC_CFG_DNUM_OFFSET 16
> +#define JZ_ADC_CFG_XYZ_OFFSET 13
> +#define JZ_ADC_CFG_SAMPLE_NUM_OFFSET 10
> +#define JZ_ADC_CFG_CLKDIV_OFFSET 5
> +
> +#define JZ_ADC_IRQ_PENDOWN BIT(4)
> +#define JZ_ADC_IRQ_PENUP BIT(3)
> +#define JZ_ADC_IRQ_TOUCH BIT(2)
> +#define JZ_ADC_IRQ_BATTERY BIT(1)
> +#define JZ_ADC_IRQ_ADCIN BIT(0)
> +
> +#define JZ_ADC_TOUCH_TYPE1 BIT(31)
> +#define JZ_ADC_TOUCH_DATA1_MASK 0xfff
> +#define JZ_ADC_TOUCH_TYPE0 BIT(15)
> +#define JZ_ADC_TOUCH_DATA0_MASK 0xfff
> +
> +#define JZ_ADC_BATTERY_MASK 0xfff
> +
> +#define JZ_ADC_ADCIN_MASK 0xfff
> +
> +struct jz4740_adc {
> + struct resource *mem;
> + void __iomem *base;
> +
> + int irq;
> +
> + struct clk *clk;
> + unsigned int clk_ref;
> +
> + struct device *hwmon;
> +
> + struct completion bat_completion;
> + struct completion adc_completion;
> +
> + spinlock_t lock;
> +};
> +
> +static irqreturn_t jz4740_adc_irq(int irq, void *data)
> +{
> + struct jz4740_adc *adc = data;
> + uint8_t status;
> +
> + status = readb(adc->base + JZ_REG_ADC_STATUS);
> +
> + if (status & JZ_ADC_IRQ_BATTERY)
> + complete(&adc->bat_completion);
> + if (status & JZ_ADC_IRQ_ADCIN)
> + complete(&adc->adc_completion);
> +
> + writeb(0xff, adc->base + JZ_REG_ADC_STATUS);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void jz4740_adc_enable_irq(struct jz4740_adc *adc, int irq)
> +{
> + unsigned long flags;
> + uint8_t val;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> +
> + val = readb(adc->base + JZ_REG_ADC_CTRL);
> + val &= ~irq;
> + writeb(val, adc->base + JZ_REG_ADC_CTRL);
> +
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +static void jz4740_adc_disable_irq(struct jz4740_adc *adc, int irq)
> +{
> + unsigned long flags;
> + uint8_t val;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> +
> + val = readb(adc->base + JZ_REG_ADC_CTRL);
> + val |= irq;
> + writeb(val, adc->base + JZ_REG_ADC_CTRL);
> +
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
You could unify the two functions above with a simple parameter. They share
almost all their code.
> +
> +static void jz4740_adc_enable_adc(struct jz4740_adc *adc, int engine)
> +{
> + unsigned long flags;
> + uint8_t val;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> +
> + val = readb(adc->base + JZ_REG_ADC_ENABLE);
> + val |= engine;
> + writeb(val, adc->base + JZ_REG_ADC_ENABLE);
> +
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +static void jz4740_adc_disable_adc(struct jz4740_adc *adc, int engine)
> +{
> + unsigned long flags;
> + uint8_t val;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> +
> + val = readb(adc->base + JZ_REG_ADC_ENABLE);
> + val &= ~engine;
> + writeb(val, adc->base + JZ_REG_ADC_ENABLE);
> +
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
The two above could be unified as well.
> +
> +static inline void jz4740_adc_set_cfg(struct jz4740_adc *adc, uint32_t mask,
> +uint32_t val)
Not sure, but the formatting of this line looks a bit odd..
> +{
> + unsigned long flags;
> + uint32_t cfg;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> +
> + cfg = readl(adc->base + JZ_REG_ADC_CFG);
> +
> + cfg &= ~mask;
> + cfg |= val;
> +
> + writel(cfg, adc->base + JZ_REG_ADC_CFG);
> +
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> + if (adc->clk_ref++ == 0)
> + clk_enable(adc->clk);
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> + if (--adc->clk_ref == 0)
> + clk_disable(adc->clk);
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
For example of mfd reasoning, nothing in this driver cares about this functionality.
To my mind it should be in the battery driver and the exports should be the
higher level functions.
Having this sort of access control in a hwmon driver seems like an unusual
approach.
> +long jz4740_adc_read_battery_voltage(struct device *dev,
> + enum jz_adc_battery_scale scale)
> +{
> + struct jz4740_adc *adc = dev_get_drvdata(dev);
> + unsigned long t;
> + long long voltage;
> + uint16_t val;
> +
> + if (!adc)
> + return -ENODEV;
> +
> + jz4740_adc_clk_enable(adc);
> +
> + if (scale == JZ_ADC_BATTERY_SCALE_2V5)
> + jz4740_adc_set_cfg(adc, JZ_ADC_CFG_BAT_MB, JZ_ADC_CFG_BAT_MB);
> + else
> + jz4740_adc_set_cfg(adc, JZ_ADC_CFG_BAT_MB, 0);
> +
> + jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_BATTERY);
> + jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_BATTERY);
> +
> + t = wait_for_completion_interruptible_timeout(&adc->bat_completion,
> + HZ);
> +
> + jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_BATTERY);
> +
> + if (t <= 0) {
> + jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_BATTERY);
> + return t ? t : -ETIMEDOUT;
> + }
> +
> + val = readw(adc->base + JZ_REG_ADC_BATTERY);
> +
> + jz4740_adc_clk_disable(adc);
> +
> + if (scale == JZ_ADC_BATTERY_SCALE_2V5)
> + voltage = (((long long)val) * 2500000LL) >> 12LL;
> + else
> + voltage = ((((long long)val) * 7395000LL) >> 12LL) + 33000LL;
> +
> + return voltage;
> +}
> +EXPORT_SYMBOL_GPL(jz4740_adc_read_battery_voltage);
> +
> +static ssize_t jz4740_adc_read_adcin(struct device *dev,
> + struct device_attribute *dev_attr,
> + char *buf)
> +{
> + struct jz4740_adc *adc = dev_get_drvdata(dev);
> + unsigned long t;
> + uint16_t val;
> +
> + jz4740_adc_clk_enable(adc);
> +
Is there a possible race here?
> + jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_ADCIN);
> + jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_ADCIN);
> +
> + t = wait_for_completion_interruptible_timeout(&adc->adc_completion,
> + HZ);
> +
> + jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_ADCIN);
> +
> + if (t <= 0) {
> + jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_ADCIN);
> + return t ? t : -ETIMEDOUT;
> + }
> +
> + val = readw(adc->base + JZ_REG_ADC_ADCIN);
> + jz4740_adc_clk_disable(adc);
> +
Does this device really use units of milivolts? (standard in hwmon).
I couldn't confirm either way via quick googling.
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, jz4740_adc_read_adcin, NULL, 0);
> +
> +static int __devinit jz4740_adc_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct jz4740_adc *adc;
> +
> + adc = kmalloc(sizeof(*adc), GFP_KERNEL);
> +
> + adc->irq = platform_get_irq(pdev, 0);
> +
> + if (adc->irq < 0) {
> + ret = adc->irq;
> + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
> + goto err_free;
> + }
> +
> + adc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!adc->mem) {
> + ret = -ENOENT;
> + dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
> + goto err_free;
> + }
> +
> + adc->mem = request_mem_region(adc->mem->start, resource_size(adc->mem),
> + pdev->name);
> +
> + if (!adc->mem) {
> + ret = -EBUSY;
> + dev_err(&pdev->dev, "Failed to request mmio memory region\n");
> + goto err_free;
> + }
> +
> + adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
> +
> + if (!adc->base) {
> + ret = -EBUSY;
> + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
> + goto err_release_mem_region;
> + }
> +
> + adc->clk = clk_get(&pdev->dev, "adc");
> +
> + if (IS_ERR(adc->clk)) {
> + ret = PTR_ERR(adc->clk);
> + dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
> + goto err_iounmap;
> + }
> +
> + init_completion(&adc->bat_completion);
> + init_completion(&adc->adc_completion);
> +
> + spin_lock_init(&adc->lock);
> +
> + adc->clk_ref = 0;
> +
> + platform_set_drvdata(pdev, adc);
> +
> + ret = request_irq(adc->irq, jz4740_adc_irq, 0, pdev->name, adc);
> +
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
> + goto err_clk_put;
> + }
> +
> + ret = device_create_file(&pdev->dev, &sensor_dev_attr_in0_input.dev_attr);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to create sysfs file: %d\n", ret);
> + goto err_free_irq;
> + }
> +
> + adc->hwmon = hwmon_device_register(&pdev->dev);
> + if (IS_ERR(adc->hwmon)) {
> + ret = PTR_ERR(adc->hwmon);
> + goto err_remove_file;
> + }
> +
> + writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
> + writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
> +
> + return 0;
> +
> +err_remove_file:
> + device_remove_file(&pdev->dev, &sensor_dev_attr_in0_input.dev_attr);
> +err_free_irq:
> + free_irq(adc->irq, adc);
> +err_clk_put:
> + clk_put(adc->clk);
> +err_iounmap:
> + platform_set_drvdata(pdev, NULL);
> + iounmap(adc->base);
> +err_release_mem_region:
> + release_mem_region(adc->mem->start, resource_size(adc->mem));
> +err_free:
> + kfree(adc);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_adc_remove(struct platform_device *pdev)
> +{
> + struct jz4740_adc *adc = platform_get_drvdata(pdev);
> +
> + hwmon_device_unregister(adc->hwmon);
> + device_remove_file(&pdev->dev, &sensor_dev_attr_in0_input.dev_attr);
> +
> + free_irq(adc->irq, adc);
> +
> + iounmap(adc->base);
> + release_mem_region(adc->mem->start, resource_size(adc->mem));
> +
> + clk_put(adc->clk);
> +
> + platform_set_drvdata(pdev, NULL);
> +
> + kfree(adc);
> +
> + return 0;
> +}
> +
> +struct platform_driver jz4740_adc_driver = {
> + .probe = jz4740_adc_probe,
> + .remove = __devexit_p(jz4740_adc_remove),
> + .driver = {
> + .name = "jz4740-adc",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_adc_init(void)
> +{
> + return platform_driver_register(&jz4740_adc_driver);
> +}
> +module_init(jz4740_adc_init);
> +
> +static void __exit jz4740_adc_exit(void)
> +{
> + platform_driver_unregister(&jz4740_adc_driver);
> +}
> +module_exit(jz4740_adc_exit);
> +
> +MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
> +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:jz4740-adc");
> diff --git a/include/linux/jz4740-adc.h b/include/linux/jz4740-adc.h
> new file mode 100644
> index 0000000..59cfe63
> --- /dev/null
> +++ b/include/linux/jz4740-adc.h
> @@ -0,0 +1,25 @@
> +
> +#ifndef __LINUX_JZ4740_ADC
> +#define __LINUX_JZ4740_ADC
> +
> +#include <linux/device.h>
> +
> +enum jz_adc_battery_scale {
> + JZ_ADC_BATTERY_SCALE_2V5, /* Mesures voltages up to 2.5V */
> + JZ_ADC_BATTERY_SCALE_7V5, /* Mesures voltages up to 7.5V */
> +};
> +
> +/*
> + * jz4740_adc_read_battery_voltage - Read battery voltage from the ADC PBAT pin
> + * @dev: Pointer to a jz4740-adc device
> + * @scale: Whether to use 2.5V or 7.5V scale
> + *
> + * Returns: Battery voltage in mircovolts
> + *
> + * Context: Process
> +*/
> +long jz4740_adc_read_battery_voltage(struct device *dev,
> + enum jz_adc_battery_scale scale);
> +
> +
> +#endif
Hi
Wan ZongShun wrote:
> 2010/6/3 Lars-Peter Clausen <[email protected]>:
>
>> This patch adds support for the RTC unit on JZ4740 SoCs.
>>
>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>> Cc: Alessandro Zummo <[email protected]>
>> Cc: Paul Gortmaker <[email protected]>
>> Cc: [email protected]
>> ---
>> drivers/rtc/Kconfig | 11 ++
>> drivers/rtc/Makefile | 1 +
>> drivers/rtc/rtc-jz4740.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 356 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/rtc/rtc-jz4740.c
>>
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index 10ba12c..d0ed7e6 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -905,4 +905,15 @@ config RTC_DRV_MPC5121
>> This driver can also be built as a module. If so, the module
>> will be called rtc-mpc5121.
>>
>> +config RTC_DRV_JZ4740
>> + tristate "Ingenic JZ4740 SoC"
>> + depends on RTC_CLASS
>> + depends on MACH_JZ4740
>> + help
>> + If you say yes here you get support for the Ingenic JZ4740 SoC RTC
>> + controller.
>> +
>> + This driver can also be buillt as a module. If so, the module
>> + will be called rtc-jz4740.
>> +
>> endif # RTC_CLASS
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 5adbba7..fedf9bb 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -47,6 +47,7 @@ obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
>> obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
>> obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
>> obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
>> +obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
>> obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
>> obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
>> obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
>> diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
>> new file mode 100644
>> index 0000000..41ab78f
>> --- /dev/null
>> +++ b/drivers/rtc/rtc-jz4740.c
>> @@ -0,0 +1,344 @@
>> +/*
>> + * Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
>> + * JZ4740 SoC RTC driver
>> + *
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along
>> + * with this program; if not, write to the Free Software Foundation, Inc.,
>> + * 675 Mass Ave, Cambridge, MA 02139, USA.
>> + *
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/rtc.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +
>> +#define JZ_REG_RTC_CTRL 0x00
>> +#define JZ_REG_RTC_SEC 0x04
>> +#define JZ_REG_RTC_SEC_ALARM 0x08
>> +#define JZ_REG_RTC_REGULATOR 0x0C
>> +#define JZ_REG_RTC_HIBERNATE 0x20
>> +#define JZ_REG_RTC_SCRATCHPAD 0x34
>> +
>> +#define JZ_RTC_CTRL_WRDY BIT(7)
>> +#define JZ_RTC_CTRL_1HZ BIT(6)
>> +#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
>> +#define JZ_RTC_CTRL_AF BIT(4)
>> +#define JZ_RTC_CTRL_AF_IRQ BIT(3)
>> +#define JZ_RTC_CTRL_AE BIT(2)
>> +#define JZ_RTC_CTRL_ENABLE BIT(0)
>> +
>> +struct jz4740_rtc {
>> + struct resource *mem;
>> + void __iomem *base;
>> +
>> + struct rtc_device *rtc;
>> +
>> + unsigned int irq;
>> +
>> + spinlock_t lock;
>> +};
>> +
>> +static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
>> +{
>> + return readl(rtc->base + reg);
>> +}
>> +
>> +static inline void jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
>> +{
>> + uint32_t ctrl;
>> + do {
>> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
>> + } while (!(ctrl & JZ_RTC_CTRL_WRDY));
>> +}
>> +
>> +
>> +static inline void jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
>> + uint32_t val)
>> +{
>> + jz4740_rtc_wait_write_ready(rtc);
>> + writel(val, rtc->base + reg);
>> +}
>> +
>> +static void jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask,
>> + uint32_t val)
>> +{
>> + unsigned long flags;
>> + uint32_t ctrl;
>> +
>> + spin_lock_irqsave(&rtc->lock, flags);
>> +
>> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
>> +
>> + /* Don't clear interrupt flags by accident */
>> + ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF;
>> +
>> + ctrl &= ~mask;
>> + ctrl |= val;
>> +
>> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl);
>> +
>> + spin_unlock_irqrestore(&rtc->lock, flags);
>> +}
>> +
>> +static inline struct jz4740_rtc *dev_to_rtc(struct device *dev)
>> +{
>> + return dev_get_drvdata(dev);
>> +}
>> +
>>
>
> Why do you need to re-implement the 'dev_to_rtc' instead of using
> 'platform_get_drvdata' provided by Linux own.'
>
I like to write self documenting code, so this documents how to get the
a pointer to the jz4740_rtc struct if you only have device. But I guess
in this case it's safe to use dev_get_drvdata directly from within the
other of the drivers functions without loosing to much.
>> +static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time)
>> +{
>> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
>> + uint32_t secs, secs2;
>> +
>> + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
>> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
>> +
>> + while (secs != secs2) {
>> + secs = secs2;
>> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
>> + }
>> +
>> + rtc_time_to_tm(secs, time);
>> +
>> + return rtc_valid_tm(time);
>> +}
>> +
>> +static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs)
>> +{
>> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
>> +
>> + if ((uint32_t)secs != secs)
>> + return -EINVAL;
>> +
>> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs);
>> +
>> + return 0;
>> +}
>> +
>> +static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
>> + uint32_t secs, secs2;
>> + uint32_t ctrl;
>> +
>> + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
>> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
>> +
>> + while (secs != secs2) {
>> + secs = secs2;
>> + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
>> + }
>> +
>> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
>> +
>> + alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE);
>> + alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF);
>> +
>> + rtc_time_to_tm(secs, &alrm->time);
>> +
>> + return rtc_valid_tm(&alrm->time);
>> +}
>> +
>> +static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
>> + unsigned long secs;
>> +
>> + rtc_tm_to_time(&alrm->time, &secs);
>> +
>> + if ((uint32_t)secs != secs)
>> + return -EINVAL;
>> +
>> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, (uint32_t)secs);
>> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE,
>> + alrm->enabled ? JZ_RTC_CTRL_AE : 0);
>> +
>> + return 0;
>> +}
>> +
>> +static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enable)
>> +{
>> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
>> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ,
>> + enable ? JZ_RTC_CTRL_1HZ_IRQ : 0);
>> + return 0;
>> +}
>> +
>> +
>> +static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
>> +{
>> + struct jz4740_rtc *rtc = dev_to_rtc(dev);
>> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ,
>> + enable ? JZ_RTC_CTRL_AF_IRQ : 0);
>> + return 0;
>> +}
>> +
>> +static struct rtc_class_ops jz4740_rtc_ops = {
>> + .read_time = jz4740_rtc_read_time,
>> + .set_mmss = jz4740_rtc_set_mmss,
>> + .read_alarm = jz4740_rtc_read_alarm,
>> + .set_alarm = jz4740_rtc_set_alarm,
>> + .update_irq_enable = jz4740_rtc_update_irq_enable,
>> + .alarm_irq_enable = jz4740_rtc_alarm_irq_enable,
>> +};
>> +
>> +static irqreturn_t jz4740_rtc_irq(int irq, void *data)
>> +{
>> + struct jz4740_rtc *rtc = data;
>> + uint32_t ctrl;
>> + unsigned long events = 0;
>> + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
>> +
>> + if (ctrl & JZ_RTC_CTRL_1HZ)
>> + events |= (RTC_UF | RTC_IRQF);
>> +
>> + if (ctrl & JZ_RTC_CTRL_AF)
>> + events |= (RTC_AF | RTC_IRQF);
>> +
>> + rtc_update_irq(rtc->rtc, 1, events);
>> +
>> + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, 0);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +void jz4740_rtc_poweroff(struct device *dev)
>> +{
>> + struct jz4740_rtc *rtc = dev_get_drvdata(dev);
>> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1);
>> +}
>> +EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff);
>> +
>> +static int __devinit jz4740_rtc_probe(struct platform_device *pdev)
>> +{
>> + int ret;
>> + struct jz4740_rtc *rtc;
>> + uint32_t scratchpad;
>> +
>> + rtc = kmalloc(sizeof(*rtc), GFP_KERNEL);
>> +
>> + rtc->irq = platform_get_irq(pdev, 0);
>> +
>> + if (rtc->irq < 0) {
>> + ret = -ENOENT;
>> + dev_err(&pdev->dev, "Failed to get platform irq\n");
>> + goto err_free;
>> + }
>> +
>> + rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!rtc->mem) {
>> + ret = -ENOENT;
>> + dev_err(&pdev->dev, "Failed to get platform mmio memory\n");
>> + goto err_free;
>> + }
>> +
>> + rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem),
>> + pdev->name);
>> +
>> + if (!rtc->mem) {
>> + ret = -EBUSY;
>> + dev_err(&pdev->dev, "Failed to request mmio memory region\n");
>> + goto err_free;
>> + }
>> +
>> + rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem));
>> +
>> + if (!rtc->base) {
>> + ret = -EBUSY;
>> + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
>> + goto err_release_mem_region;
>> + }
>> +
>> + spin_lock_init(&rtc->lock);
>> +
>> + platform_set_drvdata(pdev, rtc);
>> +
>> + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops,
>> + THIS_MODULE);
>> +
>> + if (IS_ERR(rtc->rtc)) {
>> + ret = PTR_ERR(rtc->rtc);
>> + dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret);
>> + goto err_iounmap;
>> + }
>> +
>> + ret = request_irq(rtc->irq, jz4740_rtc_irq, 0,
>> + pdev->name, rtc);
>> +
>> + if (ret) {
>> + dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret);
>> + goto err_unregister_rtc;
>> + }
>> +
>> + scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD);
>> + if (scratchpad != 0x12345678) {
>> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
>> + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
>> + }
>> +
>> + return 0;
>> +
>> +err_unregister_rtc:
>> + rtc_device_unregister(rtc->rtc);
>> +err_iounmap:
>> + platform_set_drvdata(pdev, NULL);
>> + iounmap(rtc->base);
>> +err_release_mem_region:
>> + release_mem_region(rtc->mem->start, resource_size(rtc->mem));
>> +err_free:
>> + kfree(rtc);
>> +
>> + return ret;
>> +}
>> +
>> +static int __devexit jz4740_rtc_remove(struct platform_device *pdev)
>> +{
>> + struct jz4740_rtc *rtc = platform_get_drvdata(pdev);
>> +
>> + free_irq(rtc->irq, rtc);
>> +
>> + rtc_device_unregister(rtc->rtc);
>> +
>> + iounmap(rtc->base);
>> + release_mem_region(rtc->mem->start, resource_size(rtc->mem));
>> +
>> + kfree(rtc);
>> +
>> + platform_set_drvdata(pdev, NULL);
>> +
>> + return 0;
>> +}
>> +
>> +struct platform_driver jz4740_rtc_driver = {
>> + .probe = jz4740_rtc_probe,
>> + .remove = __devexit_p(jz4740_rtc_remove),
>> + .driver = {
>> + .name = "jz4740-rtc",
>> + .owner = THIS_MODULE,
>> + },
>> +};
>> +
>> +static int __init jz4740_rtc_init(void)
>> +{
>> + return platform_driver_register(&jz4740_rtc_driver);
>>
>
> platform_driver_probe is much better here.
>
>
Ok.
>> +}
>> +module_init(jz4740_rtc_init);
>> +
>> +static void __exit jz4740_rtc_exit(void)
>> +{
>> + platform_driver_unregister(&jz4740_rtc_driver);
>> +}
>> +module_exit(jz4740_rtc_exit);
>> +
>> +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n");
>> +MODULE_ALIAS("platform:jz4740-rtc");
>> --
>> 1.5.6.5
>>
>> -
Thanks for reviewing the patch.
- Lars
Hi
Jonathan Cameron wrote:
> On 06/02/10 20:12, Lars-Peter Clausen wrote:
>
>> This patch adds support for the ADC module on JZ4740 SoCs.
>>
>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>> Cc: [email protected]
>> ---
>> drivers/hwmon/Kconfig | 11 ++
>> drivers/hwmon/Makefile | 1 +
>> drivers/hwmon/jz4740-adc.c | 423 ++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/jz4740-adc.h | 25 +++
>> 4 files changed, 460 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/hwmon/jz4740-adc.c
>> create mode 100644 include/linux/jz4740-adc.h
>>
> Hi, I'm just wondering of one wants the majority of this driver to sit in hwmon?
>
> Looks to me like a fairly classic case for something that might be best implemented
> as an mfd with the hwmon, touchscreen and battery drivers separately hanging off that.
> You might well have someone who needs the battery driver to work, but doesn't care
> about hwmon and so doesn't want to build that bit in...
>
> Just an immediate thought. Perhaps this is the best way to do things...
>
I've thought about it before and rejected the idea at that time, because
I thought it will add more abstraction then actually needed.
But at that time the adc driver was not a hwmon driver yet and thus
didn't pull in the whole hwmon framework if you only wanted to use the
battery driver.
But the more I'm thinking about it now it might actually make sense to
move the common code to a MFD driver.
> Also after a quick look. How is it used by the touchscreen driver?
> If not, please remove the reference from kconfig until it it is true.
>
There is no touchscreen driver yet. But if I'm going to remove the
reference I'm pretty sure that someone will come up and ask why it
actually is necessary to have a separate driver instead of putting all
the code into the battery driver.
> Few other bits and bobs inline.
>
>>
>> diff --git a/drivers/hwmon/jz4740-adc.c b/drivers/hwmon/jz4740-adc.c
>> new file mode 100644
>> index 0000000..635dfe9
>> --- /dev/null
>> +++ b/drivers/hwmon/jz4740-adc.c
>> @@ -0,0 +1,423 @@
>> + [...]
>> +static ssize_t jz4740_adc_read_adcin(struct device *dev,
>> + struct device_attribute *dev_attr,
>> + char *buf)
>> +{
>> + struct jz4740_adc *adc = dev_get_drvdata(dev);
>> + unsigned long t;
>> + uint16_t val;
>> +
>> + jz4740_adc_clk_enable(adc);
>> +
>>
> Is there a possible race here?
>
Where exactly?
>> + jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_ADCIN);
>> + jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_ADCIN);
>> +
>> + t = wait_for_completion_interruptible_timeout(&adc->adc_completion,
>> + HZ);
>> +
>> + jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_ADCIN);
>> +
>> + if (t <= 0) {
>> + jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_ADCIN);
>> + return t ? t : -ETIMEDOUT;
>> + }
>> +
>> + val = readw(adc->base + JZ_REG_ADC_ADCIN);
>> + jz4740_adc_clk_disable(adc);
>> +
>>
> Does this device really use units of milivolts? (standard in hwmon).
> I couldn't confirm either way via quick googling.
>
Hm, right, it does not. Interestingly the datasheet does not tell the
unit of the returned data, but I found a formula in the datasheet for a
similar SoC.
>> + return sprintf(buf, "%d\n", val);
>> +}
>> +
>> + [...]
>>
Thanks for reviewing the patch.
- Lars
On 06/05/10 20:08, Lars-Peter Clausen wrote:
> Hi
>
> Jonathan Cameron wrote:
>> On 06/02/10 20:12, Lars-Peter Clausen wrote:
>>
>>> This patch adds support for the ADC module on JZ4740 SoCs.
>>>
>>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>>> Cc: [email protected]
>>> ---
>>> drivers/hwmon/Kconfig | 11 ++
>>> drivers/hwmon/Makefile | 1 +
>>> drivers/hwmon/jz4740-adc.c | 423 ++++++++++++++++++++++++++++++++++++++++++++
>>> include/linux/jz4740-adc.h | 25 +++
>>> 4 files changed, 460 insertions(+), 0 deletions(-)
>>> create mode 100644 drivers/hwmon/jz4740-adc.c
>>> create mode 100644 include/linux/jz4740-adc.h
>>>
>> Hi, I'm just wondering of one wants the majority of this driver to sit in hwmon?
>>
>> Looks to me like a fairly classic case for something that might be best implemented
>> as an mfd with the hwmon, touchscreen and battery drivers separately hanging off that.
>> You might well have someone who needs the battery driver to work, but doesn't care
>> about hwmon and so doesn't want to build that bit in...
>>
>> Just an immediate thought. Perhaps this is the best way to do things...
>>
> I've thought about it before and rejected the idea at that time, because
> I thought it will add more abstraction then actually needed.
> But at that time the adc driver was not a hwmon driver yet and thus
> didn't pull in the whole hwmon framework if you only wanted to use the
> battery driver.
> But the more I'm thinking about it now it might actually make sense to
> move the common code to a MFD driver.
>> Also after a quick look. How is it used by the touchscreen driver?
>> If not, please remove the reference from kconfig until it it is true.
>>
> There is no touchscreen driver yet. But if I'm going to remove the
> reference I'm pretty sure that someone will come up and ask why it
> actually is necessary to have a separate driver instead of putting all
> the code into the battery driver.
Fair enough. Perhaps a comment for the patch rather than in Kconfig
as it currently is. People will enable it then go 'Why can't I now
enable the touchscreen driver?'
>> Few other bits and bobs inline.
>>
>>>
>>> diff --git a/drivers/hwmon/jz4740-adc.c b/drivers/hwmon/jz4740-adc.c
>>> new file mode 100644
>>> index 0000000..635dfe9
>>> --- /dev/null
>>> +++ b/drivers/hwmon/jz4740-adc.c
>>> @@ -0,0 +1,423 @@
>>> + [...]
>>> +static ssize_t jz4740_adc_read_adcin(struct device *dev,
>>> + struct device_attribute *dev_attr,
>>> + char *buf)
>>> +{
>>> + struct jz4740_adc *adc = dev_get_drvdata(dev);
>>> + unsigned long t;
>>> + uint16_t val;
>>> +
>>> + jz4740_adc_clk_enable(adc);
>>> +
>>>
>> Is there a possible race here?
>>
> Where exactly?
I can't recall off the top of my head if sysfs attributes can having multiple
simultaneous readers. If they can then thread two is just past the next line.
Whilst the earlier thread has passed the t = wait.... line as the interrupt has
fired. The irq is then disabled by thread 1 whilst thread 2 enables the adc.
Clearly the timeout will prevent any serious issues but the 2nd thread is going
to falsely wait a second I think... ?
>>> + jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_ADCIN);
>>> + jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_ADCIN);
>>> +
>>> + t = wait_for_completion_interruptible_timeout(&adc->adc_completion,
>>> + HZ);
>>> +
>>> + jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_ADCIN);
>>> +
>>> + if (t <= 0) {
>>> + jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_ADCIN);
>>> + return t ? t : -ETIMEDOUT;
>>> + }
>>> +
>>> + val = readw(adc->base + JZ_REG_ADC_ADCIN);
>>> + jz4740_adc_clk_disable(adc);
>>> +
>>>
>> Does this device really use units of milivolts? (standard in hwmon).
>> I couldn't confirm either way via quick googling.
>>
> Hm, right, it does not. Interestingly the datasheet does not tell the
> unit of the returned data, but I found a formula in the datasheet for a
> similar SoC.
>>> + return sprintf(buf, "%d\n", val);
>>> +}
>>> +
>>> + [...]
>>>
> Thanks for reviewing the patch.
You are welcome.
Jonathan Cameron wrote:
> On 06/05/10 20:08, Lars-Peter Clausen wrote:
>
>> Hi
>>
>> Jonathan Cameron wrote:
>>
>>> On 06/02/10 20:12, Lars-Peter Clausen wrote:
>>>
>>>
>>>> This patch adds support for the ADC module on JZ4740 SoCs.
>>>>
>>>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>>>> Cc: [email protected]
>>>> ---
>>>> drivers/hwmon/Kconfig | 11 ++
>>>> drivers/hwmon/Makefile | 1 +
>>>> drivers/hwmon/jz4740-adc.c | 423 ++++++++++++++++++++++++++++++++++++++++++++
>>>> include/linux/jz4740-adc.h | 25 +++
>>>> 4 files changed, 460 insertions(+), 0 deletions(-)
>>>> create mode 100644 drivers/hwmon/jz4740-adc.c
>>>> create mode 100644 include/linux/jz4740-adc.h
>>>>
>>>>
>>> Hi, I'm just wondering of one wants the majority of this driver to sit in hwmon?
>>>
>>> Looks to me like a fairly classic case for something that might be best implemented
>>> as an mfd with the hwmon, touchscreen and battery drivers separately hanging off that.
>>> You might well have someone who needs the battery driver to work, but doesn't care
>>> about hwmon and so doesn't want to build that bit in...
>>>
>>> Just an immediate thought. Perhaps this is the best way to do things...
>>>
>>>
>> I've thought about it before and rejected the idea at that time, because
>> I thought it will add more abstraction then actually needed.
>> But at that time the adc driver was not a hwmon driver yet and thus
>> didn't pull in the whole hwmon framework if you only wanted to use the
>> battery driver.
>> But the more I'm thinking about it now it might actually make sense to
>> move the common code to a MFD driver.
>>
>>> Also after a quick look. How is it used by the touchscreen driver?
>>> If not, please remove the reference from kconfig until it it is true.
>>>
>>>
>> There is no touchscreen driver yet. But if I'm going to remove the
>> reference I'm pretty sure that someone will come up and ask why it
>> actually is necessary to have a separate driver instead of putting all
>> the code into the battery driver.
>>
> Fair enough. Perhaps a comment for the patch rather than in Kconfig
> as it currently is. People will enable it then go 'Why can't I now
> enable the touchscreen driver?'
>
>
I guess that will work.
>>> Few other bits and bobs inline.
>>>
>>>
>>>> diff --git a/drivers/hwmon/jz4740-adc.c b/drivers/hwmon/jz4740-adc.c
>>>> new file mode 100644
>>>> index 0000000..635dfe9
>>>> --- /dev/null
>>>> +++ b/drivers/hwmon/jz4740-adc.c
>>>> @@ -0,0 +1,423 @@
>>>> + [...]
>>>> +static ssize_t jz4740_adc_read_adcin(struct device *dev,
>>>> + struct device_attribute *dev_attr,
>>>> + char *buf)
>>>> +{
>>>> + struct jz4740_adc *adc = dev_get_drvdata(dev);
>>>> + unsigned long t;
>>>> + uint16_t val;
>>>> +
>>>> + jz4740_adc_clk_enable(adc);
>>>> +
>>>>
>>>>
>>> Is there a possible race here?
>>>
>>>
>> Where exactly?
>>
> I can't recall off the top of my head if sysfs attributes can having multiple
> simultaneous readers. If they can then thread two is just past the next line.
> Whilst the earlier thread has passed the t = wait.... line as the interrupt has
> fired. The irq is then disabled by thread 1 whilst thread 2 enables the adc.
> Clearly the timeout will prevent any serious issues but the 2nd thread is going
> to falsely wait a second I think... ?
>
Hm, right. I didn't thought of that. There can be multiple simultaneous
reads.
Actually there are multiple issues with concurrent reads from adcin pin,
I guess the whole function should be protected by a mutex.
And additionally the clock is not turned off in case of an error.
- Lars
On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
> This patch adds support for the NAND controller on JZ4740 SoCs.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: David Woodhouse <[email protected]>
> Cc: [email protected]
> ---
> drivers/mtd/nand/Kconfig | 6 +
> drivers/mtd/nand/Makefile | 1 +
> drivers/mtd/nand/jz4740_nand.c | 442 +++++++++++++++++++++++++++++++++++++++
> include/linux/mtd/jz4740_nand.h | 34 +++
> 4 files changed, 483 insertions(+), 0 deletions(-)
> create mode 100644 drivers/mtd/nand/jz4740_nand.c
> create mode 100644 include/linux/mtd/jz4740_nand.h
I doubt "include/linux/" is the right place to define 'struct
jz_nand_platform_data' - it should instead live is somwhere in
arch/mips/
--
Best Regards,
Artem Bityutskiy (Артём Битюцкий)
On Wed, Jun 02, 2010 at 09:12:29PM +0200, Lars-Peter Clausen wrote:
> This patch adds support for the battery voltage measurement part of the JZ4740
> ADC unit.
>
> Signed-off-by: Lars-Peter Clausen <[email protected]>
> Cc: Anton Vorontsov <[email protected]>
> ---
Looks good. I see this is an RFC. Do you want me to apply it
or there's a newer version to be submitted?
Thanks,
--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2
Anton Vorontsov wrote:
> On Wed, Jun 02, 2010 at 09:12:29PM +0200, Lars-Peter Clausen wrote:
>
>> This patch adds support for the battery voltage measurement part of the JZ4740
>> ADC unit.
>>
>> Signed-off-by: Lars-Peter Clausen <[email protected]>
>> Cc: Anton Vorontsov <[email protected]>
>> ---
>>
>
> Looks good. I see this is an RFC. Do you want me to apply it
> or there's a newer version to be submitted?
>
> Thanks,
>
>
Hi. Thanks for taking a look at it.
I'll send a new series which has some minor cleanups for the battery
driver soon.
- Lars
On Mon, Jun 14, 2010 at 07:51:08PM +0400, Anton Vorontsov wrote:
> On Wed, Jun 02, 2010 at 09:12:29PM +0200, Lars-Peter Clausen wrote:
> > This patch adds support for the battery voltage measurement part of the JZ4740
> > ADC unit.
> >
> > Signed-off-by: Lars-Peter Clausen <[email protected]>
> > Cc: Anton Vorontsov <[email protected]>
> > ---
>
> Looks good. I see this is an RFC. Do you want me to apply it
> or there's a newer version to be submitted?
To allow for reasonable testing while this patchset is getting integrated
I suggest I apply all the patches that were acked by the respective
maintainers to the MIPS tree, feed them to -next for testing and once
that phase is complete send the whole thing to Linus.
Ralf
On Tue, Jun 15, 2010 at 06:34:32PM +0100, Ralf Baechle wrote:
> On Mon, Jun 14, 2010 at 07:51:08PM +0400, Anton Vorontsov wrote:
> > Looks good. I see this is an RFC. Do you want me to apply it
> > or there's a newer version to be submitted?
> To allow for reasonable testing while this patchset is getting integrated
> I suggest I apply all the patches that were acked by the respective
> maintainers to the MIPS tree, feed them to -next for testing and once
> that phase is complete send the whole thing to Linus.
Due to major pending changes in the audio subsystem at least the audio
subsystem changes will have merge issues in -next from that. To avoid
these I suggest putting the audio changes on a branch which can be
pulled into both trees.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi
Mark Brown wrote:
> On Tue, Jun 15, 2010 at 06:34:32PM +0100, Ralf Baechle wrote:
>> To allow for reasonable testing while this patchset is getting
>> integrated I suggest I apply all the patches that were acked by
>> the respective maintainers to the MIPS tree, feed them to -next
>> for testing and once that phase is complete send the whole thing
>> to Linus.
>
> Due to major pending changes in the audio subsystem at least the
> audio subsystem changes will have merge issues in -next from that.
> To avoid these I suggest putting the audio changes on a branch
> which can be pulled into both trees.
In my opinion the ASoC drivers can go through the ASoC tree. The core
patches will build without the ASoC patches and the ASoC drivers wont
be selectable without the core patches. Thus we wont see any build
failures due to merging both parts individually. And given that there
will be some changes required when the multi-component branch is
merged it will simplify things if the patches only go through one tree.
I've talked to Ralf and he said he is ok with it.
- - Lars
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iEYEARECAAYFAkwcPhcACgkQBX4mSR26RiOJSgCfScB+QFGTbzAqfbmvAVntvqDb
Xv8An1672tE+ZkLqBBYuhU18AEZdVmkT
=n6/A
-----END PGP SIGNATURE-----
hi troy, just checkin out....hope you're okey and doing fine. Miss you
-----
Dianne Kisky
P1- Blk. 24 Lot 8B Sta. Lucia Resettlement
Magalang, Pampanga
Mobile No.: 0927-321-3112
[email protected]
--
View this message in context: http://old.nabble.com/-RFC--PATCH-21-26--alsa%3A-ASoC%3A-Add-JZ4740-ASoC-support-tp28758941p30212275.html
Sent from the linux-kernel mailing list archive at Nabble.com.