From 2f495c398edca50ac251c134f1995a2fb3c06cb7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 21 Jun 2010 13:20:46 +1000 Subject: net/phy/marvell: Expose IDs and flags in a .h and add dns323 LEDs setup flag This moves the various known Marvell PHY IDs to include/linux/marvell_phy.h along with dev_flags definitions for use by the driver. I then added a flag that changes the PHY init code to setup the LEDs config to the values needed to operate a dns323 rev C1 NAS. I moved the existing "resistance" flag to the .h as well, though I've been unable to find whoever sets this to convert it to use that constant. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Wolfram Sang Acked-by: David S. Miller Signed-off-by: Nicolas Pitre --- include/linux/marvell_phy.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 include/linux/marvell_phy.h (limited to 'include') diff --git a/include/linux/marvell_phy.h b/include/linux/marvell_phy.h new file mode 100644 index 000000000000..2ed4fb8bbd51 --- /dev/null +++ b/include/linux/marvell_phy.h @@ -0,0 +1,20 @@ +#ifndef _MARVELL_PHY_H +#define _MARVELL_PHY_H + +/* Mask used for ID comparisons */ +#define MARVELL_PHY_ID_MASK 0xfffffff0 + +/* Known PHY IDs */ +#define MARVELL_PHY_ID_88E1101 0x01410c60 +#define MARVELL_PHY_ID_88E1112 0x01410c90 +#define MARVELL_PHY_ID_88E1111 0x01410cc0 +#define MARVELL_PHY_ID_88E1118 0x01410e10 +#define MARVELL_PHY_ID_88E1121R 0x01410cb0 +#define MARVELL_PHY_ID_88E1145 0x01410cd0 +#define MARVELL_PHY_ID_88E1240 0x01410e30 + +/* struct phy_device dev_flags definitions */ +#define MARVELL_PHY_M1145_FLAGS_RESISTANCE 0x00000001 +#define MARVELL_PHY_M1118_DNS323_LEDS 0x00000002 + +#endif /* _MARVELL_PHY_H */ -- cgit v1.2.3 From 98864ff58dd2b8ef9e72b0d2c70f34e7ff24a2ee Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 22 May 2010 23:59:11 +0100 Subject: ARM: OMAP: Convert OMAPFB and VRAM SDRAM reservation to LMB Signed-off-by: Russell King --- arch/arm/plat-omap/common.c | 4 ++-- arch/arm/plat-omap/fb.c | 30 +++++++++++++++++++----------- arch/arm/plat-omap/include/plat/vram.h | 4 ++-- drivers/video/omap2/vram.c | 33 +++++++++++++++------------------ include/linux/omapfb.h | 2 +- 5 files changed, 39 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c index 9f6bbc178a74..ebed82699eb2 100644 --- a/arch/arm/plat-omap/common.c +++ b/arch/arm/plat-omap/common.c @@ -85,8 +85,8 @@ EXPORT_SYMBOL(omap_get_var_config); void __init omap_reserve(void) { - omapfb_reserve_sdram(); - omap_vram_reserve_sdram(); + omapfb_reserve_sdram_memblock(); + omap_vram_reserve_sdram_memblock(); } /* diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 97db493904fa..0054b9501a53 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -173,25 +173,27 @@ static int check_fbmem_region(int region_idx, struct omapfb_mem_region *rg, static int valid_sdram(unsigned long addr, unsigned long size) { - struct bootmem_data *bdata = NODE_DATA(0)->bdata; - unsigned long sdram_start, sdram_end; + struct memblock_property res; - sdram_start = bdata->node_min_pfn << PAGE_SHIFT; - sdram_end = bdata->node_low_pfn << PAGE_SHIFT; - - return addr >= sdram_start && sdram_end - addr >= size; + res.base = addr; + res.size = size; + return !memblock_find(&res) && res.base == addr && res.size == size; } static int reserve_sdram(unsigned long addr, unsigned long size) { - return reserve_bootmem(addr, size, BOOTMEM_EXCLUSIVE); + if (memblock_is_region_reserved(addr, size)) + return -EBUSY; + if (memblock_reserve(addr, size)) + return -ENOMEM; + return 0; } /* * Called from map_io. We need to call to this early enough so that we * can reserve the fixed SDRAM regions before VM could get hold of them. */ -void __init omapfb_reserve_sdram(void) +void __init omapfb_reserve_sdram_memblock(void) { unsigned long reserved = 0; int i; @@ -386,7 +388,10 @@ static inline int omap_init_fb(void) arch_initcall(omap_init_fb); -void omapfb_reserve_sdram(void) {} +void omapfb_reserve_sdram_memblock(void) +{ +} + unsigned long omapfb_reserve_sram(unsigned long sram_pstart, unsigned long sram_vstart, unsigned long sram_size, @@ -402,7 +407,10 @@ void omapfb_set_platform_data(struct omapfb_platform_data *data) { } -void omapfb_reserve_sdram(void) {} +void omapfb_reserve_sdram_memblock(void) +{ +} + unsigned long omapfb_reserve_sram(unsigned long sram_pstart, unsigned long sram_vstart, unsigned long sram_size, diff --git a/arch/arm/plat-omap/include/plat/vram.h b/arch/arm/plat-omap/include/plat/vram.h index edd4987758a6..0aa4ecd12c7d 100644 --- a/arch/arm/plat-omap/include/plat/vram.h +++ b/arch/arm/plat-omap/include/plat/vram.h @@ -38,7 +38,7 @@ extern void omap_vram_get_info(unsigned long *vram, unsigned long *free_vram, extern void omap_vram_set_sdram_vram(u32 size, u32 start); extern void omap_vram_set_sram_vram(u32 size, u32 start); -extern void omap_vram_reserve_sdram(void); +extern void omap_vram_reserve_sdram_memblock(void); extern unsigned long omap_vram_reserve_sram(unsigned long sram_pstart, unsigned long sram_vstart, unsigned long sram_size, @@ -48,7 +48,7 @@ extern unsigned long omap_vram_reserve_sram(unsigned long sram_pstart, static inline void omap_vram_set_sdram_vram(u32 size, u32 start) { } static inline void omap_vram_set_sram_vram(u32 size, u32 start) { } -static inline void omap_vram_reserve_sdram(void) { } +static inline void omap_vram_reserve_sdram_memblock(void) { } static inline unsigned long omap_vram_reserve_sram(unsigned long sram_pstart, unsigned long sram_vstart, unsigned long sram_size, diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c index 3b1237ad85ed..f6fdc2085f3e 100644 --- a/drivers/video/omap2/vram.c +++ b/drivers/video/omap2/vram.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -525,10 +525,8 @@ early_param("vram", omap_vram_early_vram); * Called from map_io. We need to call to this early enough so that we * can reserve the fixed SDRAM regions before VM could get hold of them. */ -void __init omap_vram_reserve_sdram(void) +void __init omap_vram_reserve_sdram_memblock(void) { - struct bootmem_data *bdata; - unsigned long sdram_start, sdram_size; u32 paddr; u32 size = 0; @@ -555,29 +553,28 @@ void __init omap_vram_reserve_sdram(void) size = PAGE_ALIGN(size); - bdata = NODE_DATA(0)->bdata; - sdram_start = bdata->node_min_pfn << PAGE_SHIFT; - sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start; - if (paddr) { - if ((paddr & ~PAGE_MASK) || paddr < sdram_start || - paddr + size > sdram_start + sdram_size) { + struct memblock_property res; + + res.base = paddr; + res.size = size; + if ((paddr & ~PAGE_MASK) || memblock_find(&res) || + res.base != paddr || res.size != size) { pr_err("Illegal SDRAM region for VRAM\n"); return; } - if (reserve_bootmem(paddr, size, BOOTMEM_EXCLUSIVE) < 0) { - pr_err("FB: failed to reserve VRAM\n"); + if (memblock_is_region_reserved(paddr, size)) { + pr_err("FB: failed to reserve VRAM - busy\n"); return; } - } else { - if (size > sdram_size) { - pr_err("Illegal SDRAM size for VRAM\n"); + + if (memblock_reserve(paddr, size) < 0) { + pr_err("FB: failed to reserve VRAM - no memory\n"); return; } - - paddr = virt_to_phys(alloc_bootmem_pages(size)); - BUG_ON(paddr & ~PAGE_MASK); + } else { + paddr = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_REAL_LIMIT); } omap_vram_add_region(paddr, size); diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h index 9bdd91486b49..7e4cd616bcb5 100644 --- a/include/linux/omapfb.h +++ b/include/linux/omapfb.h @@ -253,7 +253,7 @@ struct omapfb_platform_data { /* in arch/arm/plat-omap/fb.c */ extern void omapfb_set_platform_data(struct omapfb_platform_data *data); extern void omapfb_set_ctrl_platform_data(void *pdata); -extern void omapfb_reserve_sdram(void); +extern void omapfb_reserve_sdram_memblock(void); #endif -- cgit v1.2.3 From ec489aa8f993f8d2ec962ce113071faac482aa27 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 2 Jun 2010 08:13:52 +0100 Subject: ARM: 6157/2: PL011 TX/RX split of LCR for ST-Ericssons derivative In the ST-Ericsson version of the PL011 the TX and RX have different control registers. Cc: Alessandro Rubini Signed-off-by: Marcin Mielczarczyk Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/serial/amba-pl011.c | 61 +++++++++++++++++++++++++++++++++++++-------- include/linux/amba/serial.h | 2 ++ 2 files changed, 52 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index eb4cb480b93e..5644cf2385bb 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -69,9 +69,11 @@ struct uart_amba_port { struct uart_port port; struct clk *clk; - unsigned int im; /* interrupt mask */ + unsigned int im; /* interrupt mask */ unsigned int old_status; - unsigned int ifls; /* vendor-specific */ + unsigned int ifls; /* vendor-specific */ + unsigned int lcrh_tx; /* vendor-specific */ + unsigned int lcrh_rx; /* vendor-specific */ bool autorts; }; @@ -79,16 +81,22 @@ struct uart_amba_port { struct vendor_data { unsigned int ifls; unsigned int fifosize; + unsigned int lcrh_tx; + unsigned int lcrh_rx; }; static struct vendor_data vendor_arm = { .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, .fifosize = 16, + .lcrh_tx = UART011_LCRH, + .lcrh_rx = UART011_LCRH, }; static struct vendor_data vendor_st = { .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, .fifosize = 64, + .lcrh_tx = ST_UART011_LCRH_TX, + .lcrh_rx = ST_UART011_LCRH_RX, }; static void pl011_stop_tx(struct uart_port *port) @@ -327,12 +335,12 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) unsigned int lcr_h; spin_lock_irqsave(&uap->port.lock, flags); - lcr_h = readw(uap->port.membase + UART011_LCRH); + lcr_h = readw(uap->port.membase + uap->lcrh_tx); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; - writew(lcr_h, uap->port.membase + UART011_LCRH); + writew(lcr_h, uap->port.membase + uap->lcrh_tx); spin_unlock_irqrestore(&uap->port.lock, flags); } @@ -393,7 +401,17 @@ static int pl011_startup(struct uart_port *port) writew(cr, uap->port.membase + UART011_CR); writew(0, uap->port.membase + UART011_FBRD); writew(1, uap->port.membase + UART011_IBRD); - writew(0, uap->port.membase + UART011_LCRH); + writew(0, uap->port.membase + uap->lcrh_rx); + if (uap->lcrh_tx != uap->lcrh_rx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(0, uap->port.membase + uap->lcrh_tx); + } writew(0, uap->port.membase + UART01x_DR); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); @@ -422,10 +440,19 @@ static int pl011_startup(struct uart_port *port) return retval; } +static void pl011_shutdown_channel(struct uart_amba_port *uap, + unsigned int lcrh) +{ + unsigned long val; + + val = readw(uap->port.membase + lcrh); + val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); + writew(val, uap->port.membase + lcrh); +} + static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned long val; /* * disable all interrupts @@ -450,9 +477,9 @@ static void pl011_shutdown(struct uart_port *port) /* * disable break condition and fifos */ - val = readw(uap->port.membase + UART011_LCRH); - val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); - writew(val, uap->port.membase + UART011_LCRH); + pl011_shutdown_channel(uap, uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) + pl011_shutdown_channel(uap, uap->lcrh_tx); /* * Shut down the clock producer @@ -561,7 +588,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L * ----------^----------^----------^----------^----- */ - writew(lcr_h, port->membase + UART011_LCRH); + writew(lcr_h, port->membase + uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(lcr_h, port->membase + uap->lcrh_tx); + } writew(old_cr, port->membase + UART011_CR); spin_unlock_irqrestore(&port->lock, flags); @@ -688,7 +725,7 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud, if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) { unsigned int lcr_h, ibrd, fbrd; - lcr_h = readw(uap->port.membase + UART011_LCRH); + lcr_h = readw(uap->port.membase + uap->lcrh_tx); *parity = 'n'; if (lcr_h & UART01x_LCRH_PEN) { @@ -800,6 +837,8 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id) } uap->ifls = vendor->ifls; + uap->lcrh_rx = vendor->lcrh_rx; + uap->lcrh_tx = vendor->lcrh_tx; uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 5a5a7fd62490..93c96a66c518 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -38,10 +38,12 @@ #define UART01x_FR 0x18 /* Flag register (Read only). */ #define UART010_IIR 0x1C /* Interrupt indentification register (Read). */ #define UART010_ICR 0x1C /* Interrupt clear register (Write). */ +#define ST_UART011_LCRH_RX 0x1C /* Rx line control register. */ #define UART01x_ILPR 0x20 /* IrDA low power counter register. */ #define UART011_IBRD 0x24 /* Integer baud rate divisor register. */ #define UART011_FBRD 0x28 /* Fractional baud rate divisor register. */ #define UART011_LCRH 0x2c /* Line control register. */ +#define ST_UART011_LCRH_TX 0x2c /* Tx Line control register. */ #define UART011_CR 0x30 /* Control register. */ #define UART011_IFLS 0x34 /* Interrupt fifo level select. */ #define UART011_IMSC 0x38 /* Interrupt mask. */ -- cgit v1.2.3 From ac3e3fb424d44109dda3b1a3459e1b30fa60ac4a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 2 Jun 2010 20:40:22 +0100 Subject: ARM: 6158/2: PL011 baudrate extension for ST-Ericssons derivative Implementation of the ST-Ericsson baudrate extension in the PL011 block. In this modified variant it is possible to change the sampling factor from 16 to 8, and thanks to this we can get higher baudrates while still using the same peripheral clock. Also replace the simple division to determine the baud divisor with DIV_ROUND_CLOSEST() rather than a simple integer division. Cc: Alessandro Rubini Cc: Jerzy Kasenberg Signed-off-by: Marcin Mielczarczyk Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/serial/amba-pl011.c | 27 +++++++++++++++++++++++++-- include/linux/amba/serial.h | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index 5644cf2385bb..f67e09da6d33 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -74,6 +74,7 @@ struct uart_amba_port { unsigned int ifls; /* vendor-specific */ unsigned int lcrh_tx; /* vendor-specific */ unsigned int lcrh_rx; /* vendor-specific */ + bool oversampling; /* vendor-specific */ bool autorts; }; @@ -83,6 +84,7 @@ struct vendor_data { unsigned int fifosize; unsigned int lcrh_tx; unsigned int lcrh_rx; + bool oversampling; }; static struct vendor_data vendor_arm = { @@ -90,6 +92,7 @@ static struct vendor_data vendor_arm = { .fifosize = 16, .lcrh_tx = UART011_LCRH, .lcrh_rx = UART011_LCRH, + .oversampling = false, }; static struct vendor_data vendor_st = { @@ -97,6 +100,7 @@ static struct vendor_data vendor_st = { .fifosize = 64, .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, + .oversampling = true, }; static void pl011_stop_tx(struct uart_port *port) @@ -499,8 +503,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = port->uartclk * 4 / baud; + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk/(uap->oversampling ? 8 : 16)); + + if (baud > port->uartclk/16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); switch (termios->c_cflag & CSIZE) { case CS5: @@ -579,6 +588,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, uap->autorts = false; } + if (uap->oversampling) { + if (baud > port->uartclk/16) + old_cr |= ST_UART011_CR_OVSFACT; + else + old_cr &= ~ST_UART011_CR_OVSFACT; + } + /* Set baud rate */ writew(quot & 0x3f, port->membase + UART011_FBRD); writew(quot >> 6, port->membase + UART011_IBRD); @@ -744,6 +760,12 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud, fbrd = readw(uap->port.membase + UART011_FBRD); *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); + + if (uap->oversampling) { + if (readw(uap->port.membase + UART011_CR) + & ST_UART011_CR_OVSFACT) + *baud *= 2; + } } } @@ -839,6 +861,7 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id) uap->ifls = vendor->ifls; uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; + uap->oversampling = vendor->oversampling; uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 93c96a66c518..e1b634b635f2 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -86,6 +86,7 @@ #define UART010_CR_TIE 0x0020 #define UART010_CR_RIE 0x0010 #define UART010_CR_MSIE 0x0008 +#define ST_UART011_CR_OVSFACT 0x0008 /* Oversampling factor */ #define UART01x_CR_IIRLP 0x0004 /* SIR low power mode */ #define UART01x_CR_SIREN 0x0002 /* SIR enable */ #define UART01x_CR_UARTEN 0x0001 /* UART enable */ -- cgit v1.2.3 From bb8f563c848faa113059973f68c24a3bb6a9585e Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 21 Jul 2010 12:53:57 +0100 Subject: ARM: 6243/1: mmci: pass power_mode to the translate_vdd callback Platforms may have some external power control which need to be controlled from board specific code. Rename the translate_vdd() callback to vdd_handler() and pass it the power mode. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 13 +++---------- include/linux/amba/mmci.h | 10 ++++++---- 2 files changed, 9 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 3eaa0e9373cd..7ae3eeeefc29 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -493,16 +493,9 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* This implicitly enables the regulator */ mmc_regulator_set_ocr(host->vcc, ios->vdd); #endif - /* - * The translate_vdd function is not used if you have - * an external regulator, or your design is really weird. - * Using it would mean sending in power control BOTH using - * a regulator AND the 4 MMCIPWR bits. If we don't have - * a regulator, we might have some other platform specific - * power control behind this translate function. - */ - if (!host->vcc && host->plat->translate_vdd) - pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); + if (host->plat->vdd_handler) + pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, + ios->power_mode); /* The ST version does not have this, fall through to POWER_ON */ if (host->hw_designer != AMBA_VENDOR_ST) { pwr |= MCI_PWR_UP; diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 7e466fe72025..ca84ce70d5d5 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -15,9 +15,10 @@ * @ocr_mask: available voltages on the 4 pins from the block, this * is ignored if a regulator is used, see the MMC_VDD_* masks in * mmc/host.h - * @translate_vdd: a callback function to translate a MMC_VDD_* - * mask into a value to be binary or:ed and written into the - * MMCIPWR register of the block + * @vdd_handler: a callback function to translate a MMC_VDD_* + * mask into a value to be binary (or set some other custom bits + * in MMCIPWR) or:ed and written into the MMCIPWR register of the + * block. May also control external power based on the power_mode. * @status: if no GPIO read function was given to the block in * gpio_wp (below) this function will be called to determine * whether a card is present in the MMC slot or not @@ -29,7 +30,8 @@ struct mmci_platform_data { unsigned int f_max; unsigned int ocr_mask; - u32 (*translate_vdd)(struct device *, unsigned int); + u32 (*vdd_handler)(struct device *, unsigned int vdd, + unsigned char power_mode); unsigned int (*status)(struct device *); int gpio_wp; int gpio_cd; -- cgit v1.2.3 From 7cfe249475fdd82ad3c2767a9b906cc775dab868 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 15 Jul 2010 10:47:14 +0100 Subject: ARM: AMBA: Add pclk support to AMBA bus infrastructure Some platforms gate the pclk (APB - the bus - clock) to the peripherals for power saving, along with the functional clock. When devices are accessed without pclk enabled, the kernel will oops. This gives them two options: 1. Leave all clocks on all the time. 2. Attempt to gate pclk along with the functional clock. (With some hardware, pclk and the functional clock are gated by a single bit in a register.) (1) has the disadvantage that it causes increased power usage, which is bad news for battery operated devices. (2) can lead to kernel oops if registers are accessed without the functional clock being enabled. So, introduce the apb_pclk signal in such a way existing drivers don't need to be updated. Essentially, this means we guarantee that: 1. pclk will be enabled whenever the driver is bound to a device - from probe() to remove() time. 2. pclk will also be enabled when reading the primecell IDs from the device. In order to allow drivers to be incrementally updated to achieve greater power savings, we provide two additional calls to allow drivers to manage the pclk - amba_pclk_enable()/amba_pclk_disable(). Signed-off-by: Russell King --- drivers/amba/bus.c | 88 +++++++++++++++++++++++++++++++++++++----------- include/linux/amba/bus.h | 11 ++++++ 2 files changed, 80 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index f60b2b6a0931..d31590e7011b 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -122,6 +122,31 @@ static int __init amba_init(void) postcore_initcall(amba_init); +static int amba_get_enable_pclk(struct amba_device *pcdev) +{ + struct clk *pclk = clk_get(&pcdev->dev, "apb_pclk"); + int ret; + + pcdev->pclk = pclk; + + if (IS_ERR(pclk)) + return PTR_ERR(pclk); + + ret = clk_enable(pclk); + if (ret) + clk_put(pclk); + + return ret; +} + +static void amba_put_disable_pclk(struct amba_device *pcdev) +{ + struct clk *pclk = pcdev->pclk; + + clk_disable(pclk); + clk_put(pclk); +} + /* * These are the device model conversion veneers; they convert the * device model structures to our more specific structures. @@ -130,17 +155,33 @@ static int amba_probe(struct device *dev) { struct amba_device *pcdev = to_amba_device(dev); struct amba_driver *pcdrv = to_amba_driver(dev->driver); - struct amba_id *id; + struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev); + int ret; - id = amba_lookup(pcdrv->id_table, pcdev); + do { + ret = amba_get_enable_pclk(pcdev); + if (ret) + break; + + ret = pcdrv->probe(pcdev, id); + if (ret == 0) + break; - return pcdrv->probe(pcdev, id); + amba_put_disable_pclk(pcdev); + } while (0); + + return ret; } static int amba_remove(struct device *dev) { + struct amba_device *pcdev = to_amba_device(dev); struct amba_driver *drv = to_amba_driver(dev->driver); - return drv->remove(to_amba_device(dev)); + int ret = drv->remove(pcdev); + + amba_put_disable_pclk(pcdev); + + return ret; } static void amba_shutdown(struct device *dev) @@ -203,7 +244,6 @@ static void amba_device_release(struct device *dev) */ int amba_device_register(struct amba_device *dev, struct resource *parent) { - u32 pid, cid; u32 size; void __iomem *tmp; int i, ret; @@ -241,25 +281,35 @@ int amba_device_register(struct amba_device *dev, struct resource *parent) goto err_release; } - /* - * Read pid and cid based on size of resource - * they are located at end of region - */ - for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8); - for (cid = 0, i = 0; i < 4; i++) - cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); + ret = amba_get_enable_pclk(dev); + if (ret == 0) { + u32 pid, cid; - iounmap(tmp); + /* + * Read pid and cid based on size of resource + * they are located at end of region + */ + for (pid = 0, i = 0; i < 4; i++) + pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << + (i * 8); + for (cid = 0, i = 0; i < 4; i++) + cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << + (i * 8); - if (cid == 0xb105f00d) - dev->periphid = pid; + amba_put_disable_pclk(dev); - if (!dev->periphid) { - ret = -ENODEV; - goto err_release; + if (cid == 0xb105f00d) + dev->periphid = pid; + + if (!dev->periphid) + ret = -ENODEV; } + iounmap(tmp); + + if (ret) + goto err_release; + ret = device_add(&dev->dev); if (ret) goto err_release; diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 8b1038607831..b0c174012436 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -14,14 +14,19 @@ #ifndef ASMARM_AMBA_H #define ASMARM_AMBA_H +#include #include +#include #include #define AMBA_NR_IRQS 2 +struct clk; + struct amba_device { struct device dev; struct resource res; + struct clk *pclk; u64 dma_mask; unsigned int periphid; unsigned int irq[AMBA_NR_IRQS]; @@ -59,6 +64,12 @@ struct amba_device *amba_find_device(const char *, struct device *, unsigned int int amba_request_regions(struct amba_device *, const char *); void amba_release_regions(struct amba_device *); +#define amba_pclk_enable(d) \ + (IS_ERR((d)->pclk) ? 0 : clk_enable((d)->pclk)) + +#define amba_pclk_disable(d) \ + do { if (!IS_ERR((d)->pclk)) clk_disable((d)->pclk); } while (0) + #define amba_config(d) (((d)->periphid >> 24) & 0xff) #define amba_rev(d) (((d)->periphid >> 20) & 0x0f) #define amba_manf(d) (((d)->periphid >> 12) & 0xff) -- cgit v1.2.3