diff options
Diffstat (limited to 'arch/sh/drivers')
30 files changed, 785 insertions, 514 deletions
diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig index 4d58eb0973d..cfd5b90a862 100644 --- a/arch/sh/drivers/dma/Kconfig +++ b/arch/sh/drivers/dma/Kconfig @@ -40,23 +40,6 @@ config NR_ONCHIP_DMA_CHANNELS DMAC supports. This will be 4 for SH7750/SH7751/Sh7750S/SH7091 and 8 for the SH7750R/SH7751R/SH7760, 12 for the SH7723/SH7780/SH7785/SH7724, default is 6. -config NR_DMA_CHANNELS_BOOL - depends on SH_DMA - bool "Override default number of maximum DMA channels" - help - This allows you to forcibly update the maximum number of supported - DMA channels for a given board. If this is unset, this will default - to the number of channels that the on-chip DMAC has. - -config NR_DMA_CHANNELS - int "Maximum number of DMA channels" - depends on SH_DMA && NR_DMA_CHANNELS_BOOL - default NR_ONCHIP_DMA_CHANNELS - help - This allows you to specify the maximum number of DMA channels to - support. Setting this to a higher value allows for cascading DMACs - with additional channels. - config SH_DMABRG bool "SH7760 DMABRG support" depends on CPU_SUBTYPE_SH7760 diff --git a/arch/sh/drivers/dma/dma-api.c b/arch/sh/drivers/dma/dma-api.c index 4a277224a87..c0eec08d8f9 100644 --- a/arch/sh/drivers/dma/dma-api.c +++ b/arch/sh/drivers/dma/dma-api.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/list.h> #include <linux/platform_device.h> #include <linux/mm.h> @@ -308,11 +309,9 @@ int dma_extend(unsigned int chan, unsigned long op, void *param) } EXPORT_SYMBOL(dma_extend); -static int dma_read_proc(char *buf, char **start, off_t off, - int len, int *eof, void *data) +static int dma_proc_show(struct seq_file *m, void *v) { - struct dma_info *info; - char *p = buf; + struct dma_info *info = v; if (list_empty(®istered_dmac_list)) return 0; @@ -332,14 +331,26 @@ static int dma_read_proc(char *buf, char **start, off_t off, if (!(channel->flags & DMA_CONFIGURED)) continue; - p += sprintf(p, "%2d: %14s %s\n", i, - info->name, channel->dev_id); + seq_printf(m, "%2d: %14s %s\n", i, + info->name, channel->dev_id); } } - return p - buf; + return 0; } +static int dma_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dma_proc_show, NULL); +} + +static const struct file_operations dma_proc_fops = { + .open = dma_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int register_dmac(struct dma_info *info) { unsigned int total_channels, i; @@ -412,8 +423,7 @@ EXPORT_SYMBOL(unregister_dmac); static int __init dma_api_init(void) { printk(KERN_NOTICE "DMA: Registering DMA API.\n"); - create_proc_read_entry("dma", 0, 0, dma_read_proc, 0); - return 0; + return proc_create("dma", 0, NULL, &dma_proc_fops) ? 0 : -ENOMEM; } subsys_initcall(dma_api_init); diff --git a/arch/sh/drivers/dma/dma-g2.c b/arch/sh/drivers/dma/dma-g2.c index af7bb589c2c..e1ab6eb3c04 100644 --- a/arch/sh/drivers/dma/dma-g2.c +++ b/arch/sh/drivers/dma/dma-g2.c @@ -170,7 +170,7 @@ static int __init g2_dma_init(void) { int ret; - ret = request_irq(HW_EVENT_G2_DMA, g2_dma_interrupt, IRQF_DISABLED, + ret = request_irq(HW_EVENT_G2_DMA, g2_dma_interrupt, 0, "g2 DMA handler", &g2_dma_info); if (unlikely(ret)) return -EINVAL; @@ -181,14 +181,14 @@ static int __init g2_dma_init(void) ret = register_dmac(&g2_dma_info); if (unlikely(ret != 0)) - free_irq(HW_EVENT_G2_DMA, 0); + free_irq(HW_EVENT_G2_DMA, &g2_dma_info); return ret; } static void __exit g2_dma_exit(void) { - free_irq(HW_EVENT_G2_DMA, 0); + free_irq(HW_EVENT_G2_DMA, &g2_dma_info); unregister_dmac(&g2_dma_info); } diff --git a/arch/sh/drivers/dma/dma-pvr2.c b/arch/sh/drivers/dma/dma-pvr2.c index 3cee58e7f1e..706a3434af7 100644 --- a/arch/sh/drivers/dma/dma-pvr2.c +++ b/arch/sh/drivers/dma/dma-pvr2.c @@ -70,7 +70,6 @@ static int pvr2_xfer_dma(struct dma_channel *chan) static struct irqaction pvr2_dma_irq = { .name = "pvr2 DMA handler", .handler = pvr2_dma_interrupt, - .flags = IRQF_DISABLED, }; static struct dma_ops pvr2_dma_ops = { diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c index 827208781ed..b2256562314 100644 --- a/arch/sh/drivers/dma/dma-sh.c +++ b/arch/sh/drivers/dma/dma-sh.c @@ -14,35 +14,72 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/io.h> #include <mach-dreamcast/mach/dma.h> #include <asm/dma.h> -#include <asm/io.h> -#include <asm/dma-sh.h> +#include <asm/dma-register.h> +#include <cpu/dma-register.h> +#include <cpu/dma.h> -#if defined(DMAE1_IRQ) -#define NR_DMAE 2 -#else -#define NR_DMAE 1 +/* + * Define the default configuration for dual address memory-memory transfer. + * The 0x400 value represents auto-request, external->external. + */ +#define RS_DUAL (DM_INC | SM_INC | 0x400 | TS_INDEX2VAL(XMIT_SZ_32BIT)) + +static unsigned long dma_find_base(unsigned int chan) +{ + unsigned long base = SH_DMAC_BASE0; + +#ifdef SH_DMAC_BASE1 + if (chan >= 6) + base = SH_DMAC_BASE1; #endif -static const char *dmae_name[] = { - "DMAC Address Error0", "DMAC Address Error1" -}; + return base; +} + +static unsigned long dma_base_addr(unsigned int chan) +{ + unsigned long base = dma_find_base(chan); + + /* Normalize offset calculation */ + if (chan >= 9) + chan -= 6; + if (chan >= 4) + base += 0x10; + + return base + (chan * 0x10); +} +#ifdef CONFIG_SH_DMA_IRQ_MULTI static inline unsigned int get_dmte_irq(unsigned int chan) { - unsigned int irq = 0; - if (chan < ARRAY_SIZE(dmte_irq_map)) - irq = dmte_irq_map[chan]; - -#if defined(CONFIG_SH_DMA_IRQ_MULTI) - if (irq > DMTE6_IRQ) - return DMTE6_IRQ; - return DMTE0_IRQ; + return chan >= 6 ? DMTE6_IRQ : DMTE0_IRQ; +} #else - return irq; + +static unsigned int dmte_irq_map[] = { + DMTE0_IRQ, DMTE0_IRQ + 1, DMTE0_IRQ + 2, DMTE0_IRQ + 3, + +#ifdef DMTE4_IRQ + DMTE4_IRQ, DMTE4_IRQ + 1, +#endif + +#ifdef DMTE6_IRQ + DMTE6_IRQ, DMTE6_IRQ + 1, +#endif + +#ifdef DMTE8_IRQ + DMTE8_IRQ, DMTE9_IRQ, DMTE10_IRQ, DMTE11_IRQ, #endif +}; + +static inline unsigned int get_dmte_irq(unsigned int chan) +{ + return dmte_irq_map[chan]; } +#endif /* * We determine the correct shift size based off of the CHCR transmit size @@ -53,9 +90,10 @@ static inline unsigned int get_dmte_irq(unsigned int chan) * iterations to complete the transfer. */ static unsigned int ts_shift[] = TS_SHIFT; + static inline unsigned int calc_xmit_shift(struct dma_channel *chan) { - u32 chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR); + u32 chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR); int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) | ((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT); @@ -73,13 +111,13 @@ static irqreturn_t dma_tei(int irq, void *dev_id) struct dma_channel *chan = dev_id; u32 chcr; - chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR); + chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR); if (!(chcr & CHCR_TE)) return IRQ_NONE; chcr &= ~(CHCR_IE | CHCR_DE); - __raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR)); + __raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR)); wake_up(&chan->wait_queue); @@ -91,13 +129,8 @@ static int sh_dmac_request_dma(struct dma_channel *chan) if (unlikely(!(chan->flags & DMA_TEI_CAPABLE))) return 0; - return request_irq(get_dmte_irq(chan->chan), dma_tei, -#if defined(CONFIG_SH_DMA_IRQ_MULTI) - IRQF_SHARED, -#else - IRQF_DISABLED, -#endif - chan->dev_id, chan); + return request_irq(get_dmte_irq(chan->chan), dma_tei, IRQF_SHARED, + chan->dev_id, chan); } static void sh_dmac_free_dma(struct dma_channel *chan) @@ -118,7 +151,7 @@ sh_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr) chan->flags &= ~DMA_TEI_CAPABLE; } - __raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR)); + __raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR)); chan->flags |= DMA_CONFIGURED; return 0; @@ -129,13 +162,13 @@ static void sh_dmac_enable_dma(struct dma_channel *chan) int irq; u32 chcr; - chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR); + chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR); chcr |= CHCR_DE; if (chan->flags & DMA_TEI_CAPABLE) chcr |= CHCR_IE; - __raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR)); + __raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR)); if (chan->flags & DMA_TEI_CAPABLE) { irq = get_dmte_irq(chan->chan); @@ -153,9 +186,9 @@ static void sh_dmac_disable_dma(struct dma_channel *chan) disable_irq(irq); } - chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR); + chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR); chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE); - __raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR)); + __raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR)); } static int sh_dmac_xfer_dma(struct dma_channel *chan) @@ -186,13 +219,13 @@ static int sh_dmac_xfer_dma(struct dma_channel *chan) */ if (chan->sar || (mach_is_dreamcast() && chan->chan == PVR2_CASCADE_CHAN)) - __raw_writel(chan->sar, (dma_base_addr[chan->chan]+SAR)); + __raw_writel(chan->sar, (dma_base_addr(chan->chan) + SAR)); if (chan->dar || (mach_is_dreamcast() && chan->chan == PVR2_CASCADE_CHAN)) - __raw_writel(chan->dar, (dma_base_addr[chan->chan] + DAR)); + __raw_writel(chan->dar, (dma_base_addr(chan->chan) + DAR)); __raw_writel(chan->count >> calc_xmit_shift(chan), - (dma_base_addr[chan->chan] + TCR)); + (dma_base_addr(chan->chan) + TCR)); sh_dmac_enable_dma(chan); @@ -201,13 +234,32 @@ static int sh_dmac_xfer_dma(struct dma_channel *chan) static int sh_dmac_get_dma_residue(struct dma_channel *chan) { - if (!(__raw_readl(dma_base_addr[chan->chan] + CHCR) & CHCR_DE)) + if (!(__raw_readl(dma_base_addr(chan->chan) + CHCR) & CHCR_DE)) return 0; - return __raw_readl(dma_base_addr[chan->chan] + TCR) + return __raw_readl(dma_base_addr(chan->chan) + TCR) << calc_xmit_shift(chan); } +/* + * DMAOR handling + */ +#if defined(CONFIG_CPU_SUBTYPE_SH7723) || \ + defined(CONFIG_CPU_SUBTYPE_SH7724) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) +#define NR_DMAOR 2 +#else +#define NR_DMAOR 1 +#endif + +/* + * DMAOR bases are broken out amongst channel groups. DMAOR0 manages + * channels 0 - 5, DMAOR1 6 - 11 (optional). + */ +#define dmaor_read_reg(n) __raw_readw(dma_find_base((n)*6)) +#define dmaor_write_reg(n, data) __raw_writew(data, dma_find_base(n)*6) + static inline int dmaor_reset(int no) { unsigned long dmaor = dmaor_read_reg(no); @@ -228,36 +280,86 @@ static inline int dmaor_reset(int no) return 0; } -#if defined(CONFIG_CPU_SH4) -static irqreturn_t dma_err(int irq, void *dummy) -{ -#if defined(CONFIG_SH_DMA_IRQ_MULTI) - int cnt = 0; - switch (irq) { -#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ) - case DMTE6_IRQ: - cnt++; +/* + * DMAE handling + */ +#ifdef CONFIG_CPU_SH4 + +#if defined(DMAE1_IRQ) +#define NR_DMAE 2 +#else +#define NR_DMAE 1 #endif - case DMTE0_IRQ: - if (dmaor_read_reg(cnt) & (DMAOR_NMIF | DMAOR_AE)) { - disable_irq(irq); - /* DMA multi and error IRQ */ - return IRQ_HANDLED; - } - default: - return IRQ_NONE; - } + +static const char *dmae_name[] = { + "DMAC Address Error0", + "DMAC Address Error1" +}; + +#ifdef CONFIG_SH_DMA_IRQ_MULTI +static inline unsigned int get_dma_error_irq(int n) +{ + return get_dmte_irq(n * 6); +} #else - dmaor_reset(0); -#if defined(CONFIG_CPU_SUBTYPE_SH7723) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) - dmaor_reset(1); + +static unsigned int dmae_irq_map[] = { + DMAE0_IRQ, + +#ifdef DMAE1_IRQ + DMAE1_IRQ, +#endif +}; + +static inline unsigned int get_dma_error_irq(int n) +{ + return dmae_irq_map[n]; +} #endif + +static irqreturn_t dma_err(int irq, void *dummy) +{ + int i; + + for (i = 0; i < NR_DMAOR; i++) + dmaor_reset(i); + disable_irq(irq); return IRQ_HANDLED; -#endif +} + +static int dmae_irq_init(void) +{ + int n; + + for (n = 0; n < NR_DMAE; n++) { + int i = request_irq(get_dma_error_irq(n), dma_err, + IRQF_SHARED, dmae_name[n], (void *)dmae_name[n]); + if (unlikely(i < 0)) { + printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]); + return i; + } + } + + return 0; +} + +static void dmae_irq_free(void) +{ + int n; + + for (n = 0; n < NR_DMAE; n++) + free_irq(get_dma_error_irq(n), NULL); +} +#else +static inline int dmae_irq_init(void) +{ + return 0; +} + +static void dmae_irq_free(void) +{ } #endif @@ -276,72 +378,34 @@ static struct dma_info sh_dmac_info = { .flags = DMAC_CHANNELS_TEI_CAPABLE, }; -#ifdef CONFIG_CPU_SH4 -static unsigned int get_dma_error_irq(int n) -{ -#if defined(CONFIG_SH_DMA_IRQ_MULTI) - return (n == 0) ? get_dmte_irq(0) : get_dmte_irq(6); -#else - return (n == 0) ? DMAE0_IRQ : -#if defined(DMAE1_IRQ) - DMAE1_IRQ; -#else - -1; -#endif -#endif -} -#endif - static int __init sh_dmac_init(void) { struct dma_info *info = &sh_dmac_info; - int i; - -#ifdef CONFIG_CPU_SH4 - int n; + int i, rc; - for (n = 0; n < NR_DMAE; n++) { - i = request_irq(get_dma_error_irq(n), dma_err, -#if defined(CONFIG_SH_DMA_IRQ_MULTI) - IRQF_SHARED, -#else - IRQF_DISABLED, -#endif - dmae_name[n], (void *)dmae_name[n]); - if (unlikely(i < 0)) { - printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]); - return i; - } - } -#endif /* CONFIG_CPU_SH4 */ + /* + * Initialize DMAE, for parts that support it. + */ + rc = dmae_irq_init(); + if (unlikely(rc != 0)) + return rc; /* * Initialize DMAOR, and clean up any error flags that may have * been set. */ - i = dmaor_reset(0); - if (unlikely(i != 0)) - return i; -#if defined(CONFIG_CPU_SUBTYPE_SH7723) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) - i = dmaor_reset(1); - if (unlikely(i != 0)) - return i; -#endif + for (i = 0; i < NR_DMAOR; i++) { + rc = dmaor_reset(i); + if (unlikely(rc != 0)) + return rc; + } return register_dmac(info); } static void __exit sh_dmac_exit(void) { -#ifdef CONFIG_CPU_SH4 - int n; - - for (n = 0; n < NR_DMAE; n++) { - free_irq(get_dma_error_irq(n), (void *)dmae_name[n]); - } -#endif /* CONFIG_CPU_SH4 */ + dmae_irq_free(); unregister_dmac(&sh_dmac_info); } diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c index 1ee631d3725..4b15feda54b 100644 --- a/arch/sh/drivers/dma/dma-sysfs.c +++ b/arch/sh/drivers/dma/dma-sysfs.c @@ -11,23 +11,25 @@ */ #include <linux/kernel.h> #include <linux/init.h> -#include <linux/sysdev.h> +#include <linux/stat.h> +#include <linux/device.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/string.h> #include <asm/dma.h> -static struct sysdev_class dma_sysclass = { +static struct bus_type dma_subsys = { .name = "dma", + .dev_name = "dma", }; -static ssize_t dma_show_devices(struct sys_device *dev, - struct sysdev_attribute *attr, char *buf) +static ssize_t dma_show_devices(struct device *dev, + struct device_attribute *attr, char *buf) { ssize_t len = 0; int i; - for (i = 0; i < MAX_DMA_CHANNELS; i++) { + for (i = 0; i < 16; i++) { struct dma_info *info = get_dma_info(i); struct dma_channel *channel = get_dma_channel(i); @@ -42,29 +44,29 @@ static ssize_t dma_show_devices(struct sys_device *dev, return len; } -static SYSDEV_ATTR(devices, S_IRUGO, dma_show_devices, NULL); +static DEVICE_ATTR(devices, S_IRUGO, dma_show_devices, NULL); -static int __init dma_sysclass_init(void) +static int __init dma_subsys_init(void) { int ret; - ret = sysdev_class_register(&dma_sysclass); + ret = subsys_system_register(&dma_subsys, NULL); if (unlikely(ret)) return ret; - return sysfs_create_file(&dma_sysclass.kset.kobj, &attr_devices.attr); + return device_create_file(dma_subsys.dev_root, &dev_attr_devices); } -postcore_initcall(dma_sysclass_init); +postcore_initcall(dma_subsys_init); -static ssize_t dma_show_dev_id(struct sys_device *dev, - struct sysdev_attribute *attr, char *buf) +static ssize_t dma_show_dev_id(struct device *dev, + struct device_attribute *attr, char *buf) { struct dma_channel *channel = to_dma_channel(dev); return sprintf(buf, "%s\n", channel->dev_id); } -static ssize_t dma_store_dev_id(struct sys_device *dev, - struct sysdev_attribute *attr, +static ssize_t dma_store_dev_id(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -72,10 +74,10 @@ static ssize_t dma_store_dev_id(struct sys_device *dev, return count; } -static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id); +static DEVICE_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id); -static ssize_t dma_store_config(struct sys_device *dev, - struct sysdev_attribute *attr, +static ssize_t dma_store_config(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -87,17 +89,17 @@ static ssize_t dma_store_config(struct sys_device *dev, return count; } -static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config); +static DEVICE_ATTR(config, S_IWUSR, NULL, dma_store_config); -static ssize_t dma_show_mode(struct sys_device *dev, - struct sysdev_attribute *attr, char *buf) +static ssize_t dma_show_mode(struct device *dev, + struct device_attribute *attr, char *buf) { struct dma_channel *channel = to_dma_channel(dev); return sprintf(buf, "0x%08x\n", channel->mode); } -static ssize_t dma_store_mode(struct sys_device *dev, - struct sysdev_attribute *attr, +static ssize_t dma_store_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -105,38 +107,38 @@ static ssize_t dma_store_mode(struct sys_device *dev, return count; } -static SYSDEV_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode); +static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode); #define dma_ro_attr(field, fmt) \ -static ssize_t dma_show_##field(struct sys_device *dev, \ - struct sysdev_attribute *attr, char *buf)\ +static ssize_t dma_show_##field(struct device *dev, \ + struct device_attribute *attr, char *buf)\ { \ struct dma_channel *channel = to_dma_channel(dev); \ return sprintf(buf, fmt, channel->field); \ } \ -static SYSDEV_ATTR(field, S_IRUGO, dma_show_##field, NULL); +static DEVICE_ATTR(field, S_IRUGO, dma_show_##field, NULL); dma_ro_attr(count, "0x%08x\n"); dma_ro_attr(flags, "0x%08lx\n"); int dma_create_sysfs_files(struct dma_channel *chan, struct dma_info *info) { - struct sys_device *dev = &chan->dev; + struct device *dev = &chan->dev; char name[16]; int ret; dev->id = chan->vchan; - dev->cls = &dma_sysclass; + dev->bus = &dma_subsys; - ret = sysdev_register(dev); + ret = device_register(dev); if (ret) return ret; - ret |= sysdev_create_file(dev, &attr_dev_id); - ret |= sysdev_create_file(dev, &attr_count); - ret |= sysdev_create_file(dev, &attr_mode); - ret |= sysdev_create_file(dev, &attr_flags); - ret |= sysdev_create_file(dev, &attr_config); + ret |= device_create_file(dev, &dev_attr_dev_id); + ret |= device_create_file(dev, &dev_attr_count); + ret |= device_create_file(dev, &dev_attr_mode); + ret |= device_create_file(dev, &dev_attr_flags); + ret |= device_create_file(dev, &dev_attr_config); if (unlikely(ret)) { dev_err(&info->pdev->dev, "Failed creating attrs\n"); @@ -149,17 +151,17 @@ int dma_create_sysfs_files(struct dma_channel *chan, struct dma_info *info) void dma_remove_sysfs_files(struct dma_channel *chan, struct dma_info *info) { - struct sys_device *dev = &chan->dev; + struct device *dev = &chan->dev; char name[16]; - sysdev_remove_file(dev, &attr_dev_id); - sysdev_remove_file(dev, &attr_count); - sysdev_remove_file(dev, &attr_mode); - sysdev_remove_file(dev, &attr_flags); - sysdev_remove_file(dev, &attr_config); + device_remove_file(dev, &dev_attr_dev_id); + device_remove_file(dev, &dev_attr_count); + device_remove_file(dev, &dev_attr_mode); + device_remove_file(dev, &dev_attr_flags); + device_remove_file(dev, &dev_attr_config); snprintf(name, sizeof(name), "dma%d", chan->chan); sysfs_remove_link(&info->pdev->dev.kobj, name); - sysdev_unregister(dev); + device_unregister(dev); } diff --git a/arch/sh/drivers/dma/dmabrg.c b/arch/sh/drivers/dma/dmabrg.c index 6ab9c4a1543..c0dd904483c 100644 --- a/arch/sh/drivers/dma/dmabrg.c +++ b/arch/sh/drivers/dma/dmabrg.c @@ -174,23 +174,23 @@ static int __init dmabrg_init(void) or = __raw_readl(DMAOR); __raw_writel(or | DMAOR_BRG | DMAOR_DMEN, DMAOR); - ret = request_irq(DMABRGI0, dmabrg_irq, IRQF_DISABLED, + ret = request_irq(DMABRGI0, dmabrg_irq, 0, "DMABRG USB address error", NULL); if (ret) goto out0; - ret = request_irq(DMABRGI1, dmabrg_irq, IRQF_DISABLED, + ret = request_irq(DMABRGI1, dmabrg_irq, 0, "DMABRG Transfer End", NULL); if (ret) goto out1; - ret = request_irq(DMABRGI2, dmabrg_irq, IRQF_DISABLED, + ret = request_irq(DMABRGI2, dmabrg_irq, 0, "DMABRG Transfer Half", NULL); if (ret == 0) return ret; - free_irq(DMABRGI1, 0); -out1: free_irq(DMABRGI0, 0); + free_irq(DMABRGI1, NULL); +out1: free_irq(DMABRGI0, NULL); out0: kfree(dmabrg_handlers); return ret; } diff --git a/arch/sh/drivers/pci/Makefile b/arch/sh/drivers/pci/Makefile index 4a59e689087..82f0a335fd1 100644 --- a/arch/sh/drivers/pci/Makefile +++ b/arch/sh/drivers/pci/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SH_RTS7751R2D) += fixups-rts7751r2d.o obj-$(CONFIG_SH_SH03) += fixups-sh03.o obj-$(CONFIG_SH_HIGHLANDER) += fixups-r7780rp.o obj-$(CONFIG_SH_SH7785LCR) += fixups-r7780rp.o +obj-$(CONFIG_SH_SDK7786) += fixups-sdk7786.o obj-$(CONFIG_SH_SDK7780) += fixups-sdk7780.o obj-$(CONFIG_SH_7780_SOLUTION_ENGINE) += fixups-sdk7780.o obj-$(CONFIG_SH_TITAN) += fixups-titan.o diff --git a/arch/sh/drivers/pci/fixups-cayman.c b/arch/sh/drivers/pci/fixups-cayman.c index b68b61d22c6..edc2fb7a5bb 100644 --- a/arch/sh/drivers/pci/fixups-cayman.c +++ b/arch/sh/drivers/pci/fixups-cayman.c @@ -5,7 +5,7 @@ #include <cpu/irq.h> #include "pci-sh5.h" -int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin) { int result = -1; diff --git a/arch/sh/drivers/pci/fixups-dreamcast.c b/arch/sh/drivers/pci/fixups-dreamcast.c index 942ef4f155f..1d1c5a227e5 100644 --- a/arch/sh/drivers/pci/fixups-dreamcast.c +++ b/arch/sh/drivers/pci/fixups-dreamcast.c @@ -28,9 +28,11 @@ #include <asm/irq.h> #include <mach/pci.h> -static void __init gapspci_fixup_resources(struct pci_dev *dev) +static void gapspci_fixup_resources(struct pci_dev *dev) { struct pci_channel *p = dev->sysdata; + struct resource res; + struct pci_bus_region region; printk(KERN_NOTICE "PCI: Fixing up device %s\n", pci_name(dev)); @@ -50,11 +52,21 @@ static void __init gapspci_fixup_resources(struct pci_dev *dev) /* * Redirect dma memory allocations to special memory window. + * + * If this GAPSPCI region were mapped by a BAR, the CPU + * phys_addr_t would be pci_resource_start(), and the bus + * address would be pci_bus_address(pci_resource_start()). + * But apparently there's no BAR mapping it, so we just + * "know" its CPU address is GAPSPCI_DMA_BASE. */ + res.start = GAPSPCI_DMA_BASE; + res.end = GAPSPCI_DMA_BASE + GAPSPCI_DMA_SIZE - 1; + res.flags = IORESOURCE_MEM; + pcibios_resource_to_bus(dev->bus, ®ion, &res); BUG_ON(!dma_declare_coherent_memory(&dev->dev, - GAPSPCI_DMA_BASE, - GAPSPCI_DMA_BASE, - GAPSPCI_DMA_SIZE, + res.start, + region.start, + resource_size(&res), DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)); break; @@ -64,7 +76,7 @@ static void __init gapspci_fixup_resources(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, gapspci_fixup_resources); -int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin) { /* * The interrupt routing semantics here are quite trivial. diff --git a/arch/sh/drivers/pci/fixups-landisk.c b/arch/sh/drivers/pci/fixups-landisk.c index bb1a6bb5149..db5b40a98e6 100644 --- a/arch/sh/drivers/pci/fixups-landisk.c +++ b/arch/sh/drivers/pci/fixups-landisk.c @@ -1,9 +1,10 @@ /* - * arch/sh/drivers/pci/ops-landisk.c + * arch/sh/drivers/pci/fixups-landisk.c * * PCI initialization for the I-O DATA Device, Inc. LANDISK board * * Copyright (C) 2006 kogiidena + * Copyright (C) 2010 Nobuhiro Iwamatsu * * May be copied or modified under the terms of the GNU General Public * License. See linux/COPYING for more information. @@ -13,9 +14,13 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pci.h> +#include <linux/sh_intc.h> #include "pci-sh4.h" -int pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) +#define PCIMCR_MRSET_OFF 0xBFFFFFFF +#define PCIMCR_RFSH_OFF 0xFFFFFFFB + +int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { /* * slot0: pin1-4 = irq5,6,7,8 @@ -23,12 +28,32 @@ int pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) * slot2: pin1-4 = irq7,8,5,6 * slot3: pin1-4 = irq8,5,6,7 */ - int irq = ((slot + pin - 1) & 0x3) + 5; + int irq = ((slot + pin - 1) & 0x3) + evt2irq(0x2a0); if ((slot | (pin - 1)) > 0x3) { - printk("PCI: Bad IRQ mapping request for slot %d pin %c\n", + printk(KERN_WARNING "PCI: Bad IRQ mapping request for slot %d pin %c\n", slot, pin - 1 + 'A'); return -1; } return irq; } + +int pci_fixup_pcic(struct pci_channel *chan) +{ + unsigned long bcr1, mcr; + + bcr1 = __raw_readl(SH7751_BCR1); + bcr1 |= 0x40080000; /* Enable Bit 19 BREQEN, set PCIC to slave */ + pci_write_reg(chan, bcr1, SH4_PCIBCR1); + + mcr = __raw_readl(SH7751_MCR); + mcr = (mcr & PCIMCR_MRSET_OFF) & PCIMCR_RFSH_OFF; + pci_write_reg(chan, mcr, SH4_PCIMCR); + + pci_write_reg(chan, 0x0c000000, SH7751_PCICONF5); + pci_write_reg(chan, 0xd0000000, SH7751_PCICONF6); + pci_write_reg(chan, 0x0c000000, SH4_PCILAR0); + pci_write_reg(chan, 0x00000000, SH4_PCILAR1); + + return 0; +} diff --git a/arch/sh/drivers/pci/fixups-r7780rp.c b/arch/sh/drivers/pci/fixups-r7780rp.c index 08b2d8658a0..57ed3f09d0c 100644 --- a/arch/sh/drivers/pci/fixups-r7780rp.c +++ b/arch/sh/drivers/pci/fixups-r7780rp.c @@ -12,13 +12,10 @@ */ #include <linux/pci.h> #include <linux/io.h> +#include <linux/sh_intc.h> #include "pci-sh4.h" -static char irq_tab[] __initdata = { - 65, 66, 67, 68, -}; - -int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { - return irq_tab[slot]; + return evt2irq(0xa20) + slot; } diff --git a/arch/sh/drivers/pci/fixups-rts7751r2d.c b/arch/sh/drivers/pci/fixups-rts7751r2d.c index e248516118a..eaddb56c45c 100644 --- a/arch/sh/drivers/pci/fixups-rts7751r2d.c +++ b/arch/sh/drivers/pci/fixups-rts7751r2d.c @@ -31,7 +31,7 @@ static char lboxre2_irq_tab[] __initdata = { IRQ_ETH0, IRQ_ETH1, IRQ_INTA, IRQ_INTD, }; -int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { if (mach_is_lboxre2()) return lboxre2_irq_tab[slot]; diff --git a/arch/sh/drivers/pci/fixups-sdk7780.c b/arch/sh/drivers/pci/fixups-sdk7780.c index 0930f988ac2..c0a015ae6ec 100644 --- a/arch/sh/drivers/pci/fixups-sdk7780.c +++ b/arch/sh/drivers/pci/fixups-sdk7780.c @@ -13,21 +13,31 @@ */ #include <linux/pci.h> #include <linux/io.h> +#include <linux/sh_intc.h> #include "pci-sh4.h" +#define IRQ_INTA evt2irq(0xa20) +#define IRQ_INTB evt2irq(0xa40) +#define IRQ_INTC evt2irq(0xa60) +#define IRQ_INTD evt2irq(0xa80) + /* IDSEL [16][17][18][19][20][21][22][23][24][25][26][27][28][29][30][31] */ static char sdk7780_irq_tab[4][16] __initdata = { /* INTA */ - { 65, 68, 67, 68, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { IRQ_INTA, IRQ_INTD, IRQ_INTC, IRQ_INTD, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1 }, /* INTB */ - { 66, 65, -1, 65, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { IRQ_INTB, IRQ_INTA, -1, IRQ_INTA, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1 }, /* INTC */ - { 67, 66, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { IRQ_INTC, IRQ_INTB, -1, IRQ_INTB, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1 }, /* INTD */ - { 68, 67, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { IRQ_INTD, IRQ_INTC, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1 }, }; -int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { return sdk7780_irq_tab[pin-1][slot]; } diff --git a/arch/sh/drivers/pci/fixups-sdk7786.c b/arch/sh/drivers/pci/fixups-sdk7786.c new file mode 100644 index 00000000000..36eb6fc3c18 --- /dev/null +++ b/arch/sh/drivers/pci/fixups-sdk7786.c @@ -0,0 +1,67 @@ +/* + * SDK7786 FPGA PCIe mux handling + * + * Copyright (C) 2010 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) "PCI: " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <mach/fpga.h> + +/* + * The SDK7786 FPGA supports mangling of most of the slots in some way or + * another. Slots 3/4 are special in that only one can be supported at a + * time, and both appear on port 3 to the PCI bus scan. Enabling slot 4 + * (the horizontal edge connector) will disable slot 3 entirely. + * + * Misconfigurations can be detected through the FPGA via the slot + * resistors to determine card presence. Hotplug remains unsupported. + */ +static unsigned int slot4en __initdata; + +char *__init pcibios_setup(char *str) +{ + if (strcmp(str, "slot4en") == 0) { + slot4en = 1; + return NULL; + } + + return str; +} + +static int __init sdk7786_pci_init(void) +{ + u16 data = fpga_read_reg(PCIECR); + + /* + * Enable slot #4 if it's been specified on the command line. + * + * Optionally reroute if slot #4 has a card present while slot #3 + * does not, regardless of command line value. + * + * Card presence is logically inverted. + */ + slot4en ?: (!(data & PCIECR_PRST4) && (data & PCIECR_PRST3)); + if (slot4en) { + pr_info("Activating PCIe slot#4 (disabling slot#3)\n"); + + data &= ~PCIECR_PCIEMUX1; + fpga_write_reg(data, PCIECR); + + /* Warn about forced rerouting if slot#3 is occupied */ + if ((data & PCIECR_PRST3) == 0) { + pr_warning("Unreachable card detected in slot#3\n"); + return -EBUSY; + } + } else + pr_info("PCIe slot#4 disabled\n"); + + return 0; +} +postcore_initcall(sdk7786_pci_init); diff --git a/arch/sh/drivers/pci/fixups-se7751.c b/arch/sh/drivers/pci/fixups-se7751.c index a4c7d3a4efc..84a88ca9200 100644 --- a/arch/sh/drivers/pci/fixups-se7751.c +++ b/arch/sh/drivers/pci/fixups-se7751.c @@ -4,13 +4,14 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/io.h> +#include <linux/sh_intc.h> #include "pci-sh4.h" -int __init pcibios_map_platform_irq(u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *, u8 slot, u8 pin) { switch (slot) { - case 0: return 13; - case 1: return 13; /* AMD Ethernet controller */ + case 0: return evt2irq(0x3a0); + case 1: return evt2irq(0x3a0); /* AMD Ethernet controller */ case 2: return -1; case 3: return -1; case 4: return -1; diff --git a/arch/sh/drivers/pci/fixups-sh03.c b/arch/sh/drivers/pci/fixups-sh03.c index 2e8a18b7ee5..16207bef9f5 100644 --- a/arch/sh/drivers/pci/fixups-sh03.c +++ b/arch/sh/drivers/pci/fixups-sh03.c @@ -2,28 +2,29 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/pci.h> +#include <linux/sh_intc.h> -int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin) { int irq; if (dev->bus->number == 0) { switch (slot) { - case 4: return 5; /* eth0 */ - case 8: return 5; /* eth1 */ - case 6: return 2; /* PCI bridge */ + case 4: return evt2irq(0x2a0); /* eth0 */ + case 8: return evt2irq(0x2a0); /* eth1 */ + case 6: return evt2irq(0x240); /* PCI bridge */ default: printk(KERN_ERR "PCI: Bad IRQ mapping request " "for slot %d\n", slot); - return 2; + return evt2irq(0x240); } } else { switch (pin) { - case 0: irq = 2; break; - case 1: irq = 2; break; - case 2: irq = 2; break; - case 3: irq = 2; break; - case 4: irq = 2; break; + case 0: irq = evt2irq(0x240); break; + case 1: irq = evt2irq(0x240); break; + case 2: irq = evt2irq(0x240); break; + case 3: irq = evt2irq(0x240); break; + case 4: irq = evt2irq(0x240); break; default: irq = -1; break; } } diff --git a/arch/sh/drivers/pci/fixups-snapgear.c b/arch/sh/drivers/pci/fixups-snapgear.c index 5a39ecc1adb..6e33ba4cd07 100644 --- a/arch/sh/drivers/pci/fixups-snapgear.c +++ b/arch/sh/drivers/pci/fixups-snapgear.c @@ -16,19 +16,20 @@ #include <linux/types.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/sh_intc.h> #include "pci-sh4.h" -int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { int irq = -1; switch (slot) { case 8: /* the PCI bridge */ break; - case 11: irq = 8; break; /* USB */ - case 12: irq = 11; break; /* PCMCIA */ - case 13: irq = 5; break; /* eth0 */ - case 14: irq = 8; break; /* eth1 */ - case 15: irq = 11; break; /* safenet (unused) */ + case 11: irq = evt2irq(0x300); break; /* USB */ + case 12: irq = evt2irq(0x360); break; /* PCMCIA */ + case 13: irq = evt2irq(0x2a0); break; /* eth0 */ + case 14: irq = evt2irq(0x300); break; /* eth1 */ + case 15: irq = evt2irq(0x360); break; /* safenet (unused) */ } printk("PCI: Mapping SnapGear IRQ for slot %d, pin %c to irq %d\n", diff --git a/arch/sh/drivers/pci/fixups-titan.c b/arch/sh/drivers/pci/fixups-titan.c index 3a79fa8254a..bd1addb1b8b 100644 --- a/arch/sh/drivers/pci/fixups-titan.c +++ b/arch/sh/drivers/pci/fixups-titan.c @@ -27,7 +27,7 @@ static char titan_irq_tab[] __initdata = { TITAN_IRQ_USB, }; -int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { int irq = titan_irq_tab[slot]; diff --git a/arch/sh/drivers/pci/ops-sh4.c b/arch/sh/drivers/pci/ops-sh4.c index 0b81999fb88..b6234203e0a 100644 --- a/arch/sh/drivers/pci/ops-sh4.c +++ b/arch/sh/drivers/pci/ops-sh4.c @@ -9,6 +9,7 @@ */ #include <linux/pci.h> #include <linux/io.h> +#include <linux/spinlock.h> #include <asm/addrspace.h> #include "pci-sh4.h" @@ -18,8 +19,6 @@ #define CONFIG_CMD(bus, devfn, where) \ (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) -static DEFINE_SPINLOCK(sh4_pci_lock); - /* * Functions for accessing PCI configuration space with type 1 accesses */ @@ -34,10 +33,10 @@ static int sh4_pci_read(struct pci_bus *bus, unsigned int devfn, * PCIPDR may only be accessed as 32 bit words, * so we must do byte alignment by hand */ - spin_lock_irqsave(&sh4_pci_lock, flags); + raw_spin_lock_irqsave(&pci_config_lock, flags); pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR); data = pci_read_reg(chan, SH4_PCIPDR); - spin_unlock_irqrestore(&sh4_pci_lock, flags); + raw_spin_unlock_irqrestore(&pci_config_lock, flags); switch (size) { case 1: @@ -69,10 +68,10 @@ static int sh4_pci_write(struct pci_bus *bus, unsigned int devfn, int shift; u32 data; - spin_lock_irqsave(&sh4_pci_lock, flags); + raw_spin_lock_irqsave(&pci_config_lock, flags); pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR); data = pci_read_reg(chan, SH4_PCIPDR); - spin_unlock_irqrestore(&sh4_pci_lock, flags); + raw_spin_unlock_irqrestore(&pci_config_lock, flags); switch (size) { case 1: diff --git a/arch/sh/drivers/pci/ops-sh7786.c b/arch/sh/drivers/pci/ops-sh7786.c index 48f594b9582..128421009e3 100644 --- a/arch/sh/drivers/pci/ops-sh7786.c +++ b/arch/sh/drivers/pci/ops-sh7786.c @@ -1,7 +1,7 @@ /* * Generic SH7786 PCI-Express operations. * - * Copyright (C) 2009 Paul Mundt + * Copyright (C) 2009 - 2010 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file "COPYING" in the main directory of this archive @@ -19,37 +19,72 @@ enum { PCI_ACCESS_WRITE, }; -static DEFINE_SPINLOCK(sh7786_pcie_lock); - static int sh7786_pcie_config_access(unsigned char access_type, struct pci_bus *bus, unsigned int devfn, int where, u32 *data) { struct pci_channel *chan = bus->sysdata; - int dev, func; + int dev, func, type, reg; dev = PCI_SLOT(devfn); func = PCI_FUNC(devfn); + type = !!bus->parent; + reg = where & ~3; if (bus->number > 255 || dev > 31 || func > 7) return PCIBIOS_FUNC_NOT_SUPPORTED; - if (devfn) - return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * While each channel has its own memory-mapped extended config + * space, it's generally only accessible when in endpoint mode. + * When in root complex mode, the controller is unable to target + * itself with either type 0 or type 1 accesses, and indeed, any + * controller initiated target transfer to its own config space + * result in a completer abort. + * + * Each channel effectively only supports a single device, but as + * the same channel <-> device access works for any PCI_SLOT() + * value, we cheat a bit here and bind the controller's config + * space to devfn 0 in order to enable self-enumeration. In this + * case the regular PAR/PDR path is sidelined and the mangled + * config access itself is initiated as a SuperHyway transaction. + */ + if (pci_is_root_bus(bus)) { + if (dev == 0) { + if (access_type == PCI_ACCESS_READ) + *data = pci_read_reg(chan, PCI_REG(reg)); + else + pci_write_reg(chan, *data, PCI_REG(reg)); + + return PCIBIOS_SUCCESSFUL; + } else if (dev > 1) + return PCIBIOS_DEVICE_NOT_FOUND; + } + + /* Clear errors */ + pci_write_reg(chan, pci_read_reg(chan, SH4A_PCIEERRFR), SH4A_PCIEERRFR); /* Set the PIO address */ pci_write_reg(chan, (bus->number << 24) | (dev << 19) | - (func << 16) | (where & ~3), SH4A_PCIEPAR); + (func << 16) | reg, SH4A_PCIEPAR); /* Enable the configuration access */ - pci_write_reg(chan, (1 << 31), SH4A_PCIEPCTLR); + pci_write_reg(chan, (1 << 31) | (type << 8), SH4A_PCIEPCTLR); + + /* Check for errors */ + if (pci_read_reg(chan, SH4A_PCIEERRFR) & 0x10) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Check for master and target aborts */ + if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28))) + return PCIBIOS_DEVICE_NOT_FOUND; if (access_type == PCI_ACCESS_READ) *data = pci_read_reg(chan, SH4A_PCIEPDR); else pci_write_reg(chan, *data, SH4A_PCIEPDR); - /* Check for master and target aborts */ - if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28))) - return PCIBIOS_DEVICE_NOT_FOUND; + /* Disable the configuration access */ + pci_write_reg(chan, 0, SH4A_PCIEPCTLR); return PCIBIOS_SUCCESSFUL; } @@ -66,11 +101,13 @@ static int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn, else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; - spin_lock_irqsave(&sh7786_pcie_lock, flags); + raw_spin_lock_irqsave(&pci_config_lock, flags); ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, devfn, where, &data); - if (ret != PCIBIOS_SUCCESSFUL) + if (ret != PCIBIOS_SUCCESSFUL) { + *val = 0xffffffff; goto out; + } if (size == 1) *val = (data >> ((where & 3) << 3)) & 0xff; @@ -84,7 +121,7 @@ static int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn, devfn, where, size, (unsigned long)*val); out: - spin_unlock_irqrestore(&sh7786_pcie_lock, flags); + raw_spin_unlock_irqrestore(&pci_config_lock, flags); return ret; } @@ -100,7 +137,7 @@ static int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn, else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; - spin_lock_irqsave(&sh7786_pcie_lock, flags); + raw_spin_lock_irqsave(&pci_config_lock, flags); ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, devfn, where, &data); if (ret != PCIBIOS_SUCCESSFUL) @@ -124,7 +161,7 @@ static int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn, ret = sh7786_pcie_config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data); out: - spin_unlock_irqrestore(&sh7786_pcie_lock, flags); + raw_spin_unlock_irqrestore(&pci_config_lock, flags); return ret; } diff --git a/arch/sh/drivers/pci/pci-sh5.c b/arch/sh/drivers/pci/pci-sh5.c index 0bf296c7879..16c1e721bf5 100644 --- a/arch/sh/drivers/pci/pci-sh5.c +++ b/arch/sh/drivers/pci/pci-sh5.c @@ -107,13 +107,13 @@ static int __init sh5pci_init(void) u32 uval; if (request_irq(IRQ_ERR, pcish5_err_irq, - IRQF_DISABLED, "PCI Error",NULL) < 0) { + 0, "PCI Error",NULL) < 0) { printk(KERN_ERR "PCISH5: Cannot hook PCI_PERR interrupt\n"); return -EINVAL; } if (request_irq(IRQ_SERR, pcish5_serr_irq, - IRQF_DISABLED, "PCI SERR interrupt", NULL) < 0) { + 0, "PCI SERR interrupt", NULL) < 0) { printk(KERN_ERR "PCISH5: Cannot hook PCI_SERR interrupt\n"); return -EINVAL; } diff --git a/arch/sh/drivers/pci/pci-sh7751.c b/arch/sh/drivers/pci/pci-sh7751.c index f98141b3b7d..86adb1e235c 100644 --- a/arch/sh/drivers/pci/pci-sh7751.c +++ b/arch/sh/drivers/pci/pci-sh7751.c @@ -81,7 +81,7 @@ static int __init sh7751_pci_init(void) unsigned int id; u32 word, reg; - printk(KERN_NOTICE "PCI: Starting intialization.\n"); + printk(KERN_NOTICE "PCI: Starting initialization.\n"); chan->reg_base = 0xfe200000; diff --git a/arch/sh/drivers/pci/pci-sh7751.h b/arch/sh/drivers/pci/pci-sh7751.h index 4983a4d2035..5ede38c330d 100644 --- a/arch/sh/drivers/pci/pci-sh7751.h +++ b/arch/sh/drivers/pci/pci-sh7751.h @@ -61,7 +61,7 @@ #define SH7751_PCICONF3_BIST7 0x80000000 /* Bist Supported */ #define SH7751_PCICONF3_BIST6 0x40000000 /* Bist Executing */ #define SH7751_PCICONF3_BIST3_0 0x0F000000 /* Bist Passed */ - #define SH7751_PCICONF3_HD7 0x00800000 /* Single Funtion device */ + #define SH7751_PCICONF3_HD7 0x00800000 /* Single Function device */ #define SH7751_PCICONF3_HD6_0 0x007F0000 /* Configuration Layout */ #define SH7751_PCICONF3_LAT 0x0000FF00 /* Latency Timer */ #define SH7751_PCICONF3_CLS 0x000000FF /* Cache Line Size */ diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c index ffdcbf10b95..5a6dab6e27d 100644 --- a/arch/sh/drivers/pci/pci-sh7780.c +++ b/arch/sh/drivers/pci/pci-sh7780.c @@ -21,6 +21,13 @@ #include <asm/mmu.h> #include <asm/sizes.h> +#if defined(CONFIG_CPU_BIG_ENDIAN) +# define PCICR_ENDIANNESS SH4_PCICR_BSWP +#else +# define PCICR_ENDIANNESS 0 +#endif + + static struct resource sh7785_pci_resources[] = { { .name = "PCI IO", @@ -74,7 +81,7 @@ struct pci_errors { { SH4_PCIINT_MLCK, "master lock error" }, { SH4_PCIINT_TABT, "target-target abort" }, { SH4_PCIINT_TRET, "target retry time out" }, - { SH4_PCIINT_MFDE, "master function disable erorr" }, + { SH4_PCIINT_MFDE, "master function disable error" }, { SH4_PCIINT_PRTY, "address parity error" }, { SH4_PCIINT_SERR, "SERR" }, { SH4_PCIINT_TWDP, "data parity error for target write" }, @@ -172,7 +179,7 @@ static int __init sh7780_pci_setup_irqs(struct pci_channel *hose) PCI_STATUS_SIG_TARGET_ABORT | \ PCI_STATUS_PARITY, hose->reg_base + PCI_STATUS); - ret = request_irq(hose->serr_irq, sh7780_pci_serr_irq, IRQF_DISABLED, + ret = request_irq(hose->serr_irq, sh7780_pci_serr_irq, 0, "PCI SERR interrupt", hose); if (unlikely(ret)) { printk(KERN_ERR "PCI: Failed hooking SERR IRQ\n"); @@ -246,7 +253,7 @@ static int __init sh7780_pci_init(void) const char *type; int ret, i; - printk(KERN_NOTICE "PCI: Starting intialization.\n"); + printk(KERN_NOTICE "PCI: Starting initialization.\n"); chan->reg_base = 0xfe040000; @@ -254,7 +261,7 @@ static int __init sh7780_pci_init(void) __raw_writel(PCIECR_ENBL, PCIECR); /* Reset */ - __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_PRST, + __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_PRST | PCICR_ENDIANNESS, chan->reg_base + SH4_PCICR); /* @@ -290,7 +297,8 @@ static int __init sh7780_pci_init(void) * Now throw it in to register initialization mode and * start the real work. */ - __raw_writel(SH4_PCICR_PREFIX, chan->reg_base + SH4_PCICR); + __raw_writel(SH4_PCICR_PREFIX | PCICR_ENDIANNESS, + chan->reg_base + SH4_PCICR); memphys = __pa(memory_start); memsize = roundup_pow_of_two(memory_end - memory_start); @@ -380,7 +388,8 @@ static int __init sh7780_pci_init(void) * Initialization mode complete, release the control register and * enable round robin mode to stop device overruns/starvation. */ - __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_CFIN | SH4_PCICR_FTO, + __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_CFIN | SH4_PCICR_FTO | + PCICR_ENDIANNESS, chan->reg_base + SH4_PCICR); ret = register_pci_controller(chan); diff --git a/arch/sh/drivers/pci/pci-sh7780.h b/arch/sh/drivers/pci/pci-sh7780.h index 205dcbefe27..1742e2c9db7 100644 --- a/arch/sh/drivers/pci/pci-sh7780.h +++ b/arch/sh/drivers/pci/pci-sh7780.h @@ -12,12 +12,6 @@ #ifndef _PCI_SH7780_H_ #define _PCI_SH7780_H_ -#define PCI_VENDOR_ID_RENESAS 0x1912 -#define PCI_DEVICE_ID_RENESAS_SH7781 0x0001 -#define PCI_DEVICE_ID_RENESAS_SH7780 0x0002 -#define PCI_DEVICE_ID_RENESAS_SH7763 0x0004 -#define PCI_DEVICE_ID_RENESAS_SH7785 0x0007 - /* SH7780 Control Registers */ #define PCIECR 0xFE000008 #define PCIECR_ENBL 0x01 diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 1e9598d2bbf..1bc09ee7948 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -19,6 +19,8 @@ #include <linux/dma-debug.h> #include <linux/io.h> #include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/export.h> unsigned long PCIBIOS_MIN_IO = 0x0000; unsigned long PCIBIOS_MIN_MEM = 0; @@ -30,19 +32,34 @@ static struct pci_channel *hose_head, **hose_tail = &hose_head; static int pci_initialized; -static void __devinit pcibios_scanbus(struct pci_channel *hose) +static void pcibios_scanbus(struct pci_channel *hose) { static int next_busno; static int need_domain_info; + LIST_HEAD(resources); + struct resource *res; + resource_size_t offset; + int i; struct pci_bus *bus; - bus = pci_scan_bus(next_busno, hose->pci_ops, hose); + for (i = 0; i < hose->nr_resources; i++) { + res = hose->resources + i; + offset = 0; + if (res->flags & IORESOURCE_IO) + offset = hose->io_offset; + else if (res->flags & IORESOURCE_MEM) + offset = hose->mem_offset; + pci_add_resource_offset(&resources, res, offset); + } + + bus = pci_scan_root_bus(NULL, next_busno, hose->pci_ops, hose, + &resources); hose->bus = bus; need_domain_info = need_domain_info || hose->index; hose->need_domain_info = need_domain_info; if (bus) { - next_busno = bus->subordinate + 1; + next_busno = bus->busn_res.end + 1; /* Don't allow 8-bit bus number overflow inside the hose - reserve some space for bridges. */ if (next_busno > 224) { @@ -52,13 +69,19 @@ static void __devinit pcibios_scanbus(struct pci_channel *hose) pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); - pci_enable_bridges(bus); + } else { + pci_free_resource_list(&resources); } } +/* + * This interrupt-safe spinlock protects all accesses to PCI + * configuration space. + */ +DEFINE_RAW_SPINLOCK(pci_config_lock); static DEFINE_MUTEX(pci_scan_mutex); -int __devinit register_pci_controller(struct pci_channel *hose) +int register_pci_controller(struct pci_channel *hose) { int i; @@ -78,7 +101,7 @@ int __devinit register_pci_controller(struct pci_channel *hose) hose_tail = &hose->next; /* - * Do not panic here but later - this might hapen before console init. + * Do not panic here but later - this might happen before console init. */ if (!hose->io_map_base) { printk(KERN_WARNING @@ -128,50 +151,12 @@ static int __init pcibios_init(void) } subsys_initcall(pcibios_init); -static void pcibios_fixup_device_resources(struct pci_dev *dev, - struct pci_bus *bus) -{ - /* Update device resources. */ - struct pci_channel *hose = bus->sysdata; - unsigned long offset = 0; - int i; - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - if (!dev->resource[i].start) - continue; - if (dev->resource[i].flags & IORESOURCE_IO) - offset = hose->io_offset; - else if (dev->resource[i].flags & IORESOURCE_MEM) - offset = hose->mem_offset; - - dev->resource[i].start += offset; - dev->resource[i].end += offset; - } -} - /* * Called after each bus is probed, but before its children * are examined. */ -void __devinit pcibios_fixup_bus(struct pci_bus *bus) +void pcibios_fixup_bus(struct pci_bus *bus) { - struct pci_dev *dev = bus->self; - struct list_head *ln; - struct pci_channel *hose = bus->sysdata; - - if (!dev) { - int i; - - for (i = 0; i < hose->nr_resources; i++) - bus->resource[i] = hose->resources + i; - } - - for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { - dev = pci_dev_b(ln); - - if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) - pcibios_fixup_device_resources(dev, bus); - } } /* @@ -201,105 +186,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return start; } -void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - struct pci_channel *hose = dev->sysdata; - unsigned long offset = 0; - - if (res->flags & IORESOURCE_IO) - offset = hose->io_offset; - else if (res->flags & IORESOURCE_MEM) - offset = hose->mem_offset; - - region->start = res->start - offset; - region->end = res->end - offset; -} - -void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) -{ - struct pci_channel *hose = dev->sysdata; - unsigned long offset = 0; - - if (res->flags & IORESOURCE_IO) - offset = hose->io_offset; - else if (res->flags & IORESOURCE_MEM) - offset = hose->mem_offset; - - res->start = region->start + offset; - res->end = region->end + offset; -} - -int pcibios_enable_device(struct pci_dev *dev, int mask) -{ - u16 cmd, old_cmd; - int idx; - struct resource *r; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - old_cmd = cmd; - for (idx=0; idx < PCI_NUM_RESOURCES; idx++) { - /* Only set up the requested stuff */ - if (!(mask & (1<<idx))) - continue; - - r = &dev->resource[idx]; - if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) - continue; - if ((idx == PCI_ROM_RESOURCE) && - (!(r->flags & IORESOURCE_ROM_ENABLE))) - continue; - if (!r->start && r->end) { - printk(KERN_ERR "PCI: Device %s not available " - "because of resource collisions\n", - pci_name(dev)); - return -EINVAL; - } - if (r->flags & IORESOURCE_IO) - cmd |= PCI_COMMAND_IO; - if (r->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; - } - if (cmd != old_cmd) { - printk("PCI: Enabling device %s (%04x -> %04x)\n", - pci_name(dev), old_cmd, cmd); - pci_write_config_word(dev, PCI_COMMAND, cmd); - } - return 0; -} - -/* - * If we set up a device for bus mastering, we need to check and set - * the latency timer as it may not be properly set. - */ -static unsigned int pcibios_max_latency = 255; - -void pcibios_set_master(struct pci_dev *dev) -{ - u8 lat; - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); - if (lat < 16) - lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; - else if (lat > pcibios_max_latency) - lat = pcibios_max_latency; - else - return; - printk(KERN_INFO "PCI: Setting latency timer of device %s to %d\n", - pci_name(dev), lat); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); -} - -void __init pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - -char * __devinit pcibios_setup(char *str) -{ - return str; -} - static void __init pcibios_bus_report_status_early(struct pci_channel *hose, int top_bus, int current_bus, @@ -403,46 +289,22 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, #ifndef CONFIG_GENERIC_IOMAP -static void __iomem *ioport_map_pci(struct pci_dev *dev, - unsigned long port, unsigned int nr) +void __iomem *__pci_ioport_map(struct pci_dev *dev, + unsigned long port, unsigned int nr) { struct pci_channel *chan = dev->sysdata; if (unlikely(!chan->io_map_base)) { - chan->io_map_base = generic_io_base; + chan->io_map_base = sh_io_port_base; if (pci_domains_supported) panic("To avoid data corruption io_map_base MUST be " "set with multiple PCI domains."); } - return (void __iomem *)(chan->io_map_base + port); } -void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) -{ - resource_size_t start = pci_resource_start(dev, bar); - resource_size_t len = pci_resource_len(dev, bar); - unsigned long flags = pci_resource_flags(dev, bar); - - if (unlikely(!len || !start)) - return NULL; - if (maxlen && len > maxlen) - len = maxlen; - - if (flags & IORESOURCE_IO) - return ioport_map_pci(dev, start, len); - if (flags & IORESOURCE_MEM) { - if (flags & IORESOURCE_CACHEABLE) - return ioremap(start, len); - return ioremap_nocache(start, len); - } - - return NULL; -} -EXPORT_SYMBOL(pci_iomap); - void pci_iounmap(struct pci_dev *dev, void __iomem *addr) { iounmap(addr); @@ -451,9 +313,5 @@ EXPORT_SYMBOL(pci_iounmap); #endif /* CONFIG_GENERIC_IOMAP */ -#ifdef CONFIG_HOTPLUG -EXPORT_SYMBOL(pcibios_resource_to_bus); -EXPORT_SYMBOL(pcibios_bus_to_resource); EXPORT_SYMBOL(PCIBIOS_MIN_IO); EXPORT_SYMBOL(PCIBIOS_MIN_MEM); -#endif diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c index 68cb9b0ac9d..a162a7f86b2 100644 --- a/arch/sh/drivers/pci/pcie-sh7786.c +++ b/arch/sh/drivers/pci/pcie-sh7786.c @@ -1,23 +1,30 @@ /* * Low-Level PCI Express Support for the SH7786 * - * Copyright (C) 2009 - 2010 Paul Mundt + * Copyright (C) 2009 - 2011 Paul Mundt * * 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. */ +#define pr_fmt(fmt) "PCI: " fmt + #include <linux/pci.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/io.h> +#include <linux/async.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/clk.h> +#include <linux/sh_clk.h> +#include <linux/sh_intc.h> #include "pcie-sh7786.h" #include <asm/sizes.h> struct sh7786_pcie_port { struct pci_channel *hose; + struct clk *fclk, phy_clk; unsigned int index; int endpoint; int link; @@ -28,7 +35,7 @@ static unsigned int nr_ports; static struct sh7786_pcie_hwops { int (*core_init)(void); - int (*port_init_hw)(struct sh7786_pcie_port *port); + async_func_t port_init_hw; } *sh7786_pcie_hwops; static struct resource sh7786_pci0_resources[] = { @@ -51,6 +58,7 @@ static struct resource sh7786_pci0_resources[] = { .name = "PCIe0 MEM 2", .start = 0xfe100000, .end = 0xfe100000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, }, }; @@ -74,6 +82,7 @@ static struct resource sh7786_pci1_resources[] = { .name = "PCIe1 MEM 2", .start = 0xfe300000, .end = 0xfe300000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, }, }; @@ -82,6 +91,7 @@ static struct resource sh7786_pci2_resources[] = { .name = "PCIe2 IO", .start = 0xfc800000, .end = 0xfc800000 + SZ_4M - 1, + .flags = IORESOURCE_IO, }, { .name = "PCIe2 MEM 0", .start = 0x80000000, @@ -96,6 +106,7 @@ static struct resource sh7786_pci2_resources[] = { .name = "PCIe2 MEM 2", .start = 0xfcd00000, .end = 0xfcd00000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, }, }; @@ -117,7 +128,29 @@ static struct pci_channel sh7786_pci_channels[] = { DEFINE_CONTROLLER(0xfcc00000, 2), }; -static int phy_wait_for_ack(struct pci_channel *chan) +static struct clk fixed_pciexclkp = { + .rate = 100000000, /* 100 MHz reference clock */ +}; + +static void sh7786_pci_fixup(struct pci_dev *dev) +{ + /* + * Prevent enumeration of root complex resources. + */ + if (pci_is_root_bus(dev->bus) && dev->devfn == 0) { + int i; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + dev->resource[i].start = 0; + dev->resource[i].end = 0; + dev->resource[i].flags = 0; + } + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_SH7786, + sh7786_pci_fixup); + +static int __init phy_wait_for_ack(struct pci_channel *chan) { unsigned int timeout = 100; @@ -131,7 +164,7 @@ static int phy_wait_for_ack(struct pci_channel *chan) return -ETIMEDOUT; } -static int pci_wait_for_irq(struct pci_channel *chan, unsigned int mask) +static int __init pci_wait_for_irq(struct pci_channel *chan, unsigned int mask) { unsigned int timeout = 100; @@ -145,19 +178,14 @@ static int pci_wait_for_irq(struct pci_channel *chan, unsigned int mask) return -ETIMEDOUT; } -static void phy_write_reg(struct pci_channel *chan, unsigned int addr, - unsigned int lane, unsigned int data) +static void __init phy_write_reg(struct pci_channel *chan, unsigned int addr, + unsigned int lane, unsigned int data) { - unsigned long phyaddr, ctrl; + unsigned long phyaddr; phyaddr = (1 << BITS_CMD) + ((lane & 0xf) << BITS_LANE) + ((addr & 0xff) << BITS_ADR); - /* Enable clock */ - ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR); - ctrl |= (1 << BITS_CKE); - pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR); - /* Set write data */ pci_write_reg(chan, data, SH4A_PCIEPHYDOUTR); pci_write_reg(chan, phyaddr, SH4A_PCIEPHYADRR); @@ -165,20 +193,74 @@ static void phy_write_reg(struct pci_channel *chan, unsigned int addr, phy_wait_for_ack(chan); /* Clear command */ + pci_write_reg(chan, 0, SH4A_PCIEPHYDOUTR); pci_write_reg(chan, 0, SH4A_PCIEPHYADRR); phy_wait_for_ack(chan); +} - /* Disable clock */ - ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR); - ctrl &= ~(1 << BITS_CKE); - pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR); +static int __init pcie_clk_init(struct sh7786_pcie_port *port) +{ + struct pci_channel *chan = port->hose; + struct clk *clk; + char fclk_name[16]; + int ret; + + /* + * First register the fixed clock + */ + ret = clk_register(&fixed_pciexclkp); + if (unlikely(ret != 0)) + return ret; + + /* + * Grab the port's function clock, which the PHY clock depends + * on. clock lookups don't help us much at this point, since no + * dev_id is available this early. Lame. + */ + snprintf(fclk_name, sizeof(fclk_name), "pcie%d_fck", port->index); + + port->fclk = clk_get(NULL, fclk_name); + if (IS_ERR(port->fclk)) { + ret = PTR_ERR(port->fclk); + goto err_fclk; + } + + clk_enable(port->fclk); + + /* + * And now, set up the PHY clock + */ + clk = &port->phy_clk; + + memset(clk, 0, sizeof(struct clk)); + + clk->parent = &fixed_pciexclkp; + clk->enable_reg = (void __iomem *)(chan->reg_base + SH4A_PCIEPHYCTLR); + clk->enable_bit = BITS_CKE; + + ret = sh_clk_mstp_register(clk, 1); + if (unlikely(ret < 0)) + goto err_phy; + + return 0; + +err_phy: + clk_disable(port->fclk); + clk_put(port->fclk); +err_fclk: + clk_unregister(&fixed_pciexclkp); + + return ret; } -static int phy_init(struct pci_channel *chan) +static int __init phy_init(struct sh7786_pcie_port *port) { + struct pci_channel *chan = port->hose; unsigned int timeout = 100; + clk_enable(&port->phy_clk); + /* Initialize the phy */ phy_write_reg(chan, 0x60, 0xf, 0x004b008b); phy_write_reg(chan, 0x61, 0xf, 0x00007b41); @@ -187,9 +269,13 @@ static int phy_init(struct pci_channel *chan) phy_write_reg(chan, 0x66, 0xf, 0x00000010); phy_write_reg(chan, 0x74, 0xf, 0x0007001c); phy_write_reg(chan, 0x79, 0xf, 0x01fc000d); + phy_write_reg(chan, 0xb0, 0xf, 0x00000610); /* Deassert Standby */ - phy_write_reg(chan, 0x67, 0xf, 0x00000400); + phy_write_reg(chan, 0x67, 0x1, 0x00000400); + + /* Disable clock */ + clk_disable(&port->phy_clk); while (timeout--) { if (pci_read_reg(chan, SH4A_PCIEPHYSR)) @@ -201,22 +287,33 @@ static int phy_init(struct pci_channel *chan) return -ETIMEDOUT; } -static int pcie_init(struct sh7786_pcie_port *port) +static void __init pcie_reset(struct sh7786_pcie_port *port) +{ + struct pci_channel *chan = port->hose; + + pci_write_reg(chan, 1, SH4A_PCIESRSTR); + pci_write_reg(chan, 0, SH4A_PCIETCTLR); + pci_write_reg(chan, 0, SH4A_PCIESRSTR); + pci_write_reg(chan, 0, SH4A_PCIETXVC0SR); +} + +static int __init pcie_init(struct sh7786_pcie_port *port) { struct pci_channel *chan = port->hose; unsigned int data; phys_addr_t memphys; size_t memsize; - int ret, i; + int ret, i, win; /* Begin initialization */ - pci_write_reg(chan, 0, SH4A_PCIETCTLR); + pcie_reset(port); - /* Initialize as type1. */ - data = pci_read_reg(chan, SH4A_PCIEPCICONF3); - data &= ~(0x7f << 16); - data |= PCI_HEADER_TYPE_BRIDGE << 16; - pci_write_reg(chan, data, SH4A_PCIEPCICONF3); + /* + * Initial header for port config space is type 1, set the device + * class to match. Hardware takes care of propagating the IDSETR + * settings, so there is no need to bother with a quirk. + */ + pci_write_reg(chan, PCI_CLASS_BRIDGE_PCI << 16, SH4A_PCIEIDSETR1); /* Initialize default capabilities. */ data = pci_read_reg(chan, SH4A_PCIEEXPCAP0); @@ -268,30 +365,33 @@ static int pcie_init(struct sh7786_pcie_port *port) * LAR1/LAMR1. */ if (memsize > SZ_512M) { - __raw_writel(memphys + SZ_512M, chan->reg_base + SH4A_PCIELAR1); - __raw_writel(((memsize - SZ_512M) - SZ_256) | 1, - chan->reg_base + SH4A_PCIELAMR1); + pci_write_reg(chan, memphys + SZ_512M, SH4A_PCIELAR1); + pci_write_reg(chan, ((memsize - SZ_512M) - SZ_256) | 1, + SH4A_PCIELAMR1); memsize = SZ_512M; } else { /* * Otherwise just zero it out and disable it. */ - __raw_writel(0, chan->reg_base + SH4A_PCIELAR1); - __raw_writel(0, chan->reg_base + SH4A_PCIELAMR1); + pci_write_reg(chan, 0, SH4A_PCIELAR1); + pci_write_reg(chan, 0, SH4A_PCIELAMR1); } /* * LAR0/LAMR0 covers up to the first 512MB, which is enough to * cover all of lowmem on most platforms. */ - __raw_writel(memphys, chan->reg_base + SH4A_PCIELAR0); - __raw_writel((memsize - SZ_256) | 1, chan->reg_base + SH4A_PCIELAMR0); + pci_write_reg(chan, memphys, SH4A_PCIELAR0); + pci_write_reg(chan, (memsize - SZ_256) | 1, SH4A_PCIELAMR0); /* Finish initialization */ data = pci_read_reg(chan, SH4A_PCIETCTLR); data |= 0x1; pci_write_reg(chan, data, SH4A_PCIETCTLR); + /* Let things settle down a bit.. */ + mdelay(100); + /* Enable DL_Active Interrupt generation */ data = pci_read_reg(chan, SH4A_PCIEDLINTENR); data |= PCIEDLINTENR_DLL_ACT_ENABLE; @@ -302,9 +402,12 @@ static int pcie_init(struct sh7786_pcie_port *port) data |= PCIEMACCTLR_SCR_DIS | (0xff << 16); pci_write_reg(chan, data, SH4A_PCIEMACCTLR); + /* + * This will timeout if we don't have a link, but we permit the + * port to register anyways in order to support hotplug on future + * hardware. + */ ret = pci_wait_for_irq(chan, MASK_INT_TX_CTRL); - if (unlikely(ret != 0)) - return -ENODEV; data = pci_read_reg(chan, SH4A_PCIEPCICONF1); data &= ~(PCI_STATUS_DEVSEL_MASK << 16); @@ -317,70 +420,103 @@ static int pcie_init(struct sh7786_pcie_port *port) wmb(); - data = pci_read_reg(chan, SH4A_PCIEMACSR); - printk(KERN_NOTICE "PCI: PCIe#%d link width %d\n", - port->index, (data >> 20) & 0x3f); - + if (ret == 0) { + data = pci_read_reg(chan, SH4A_PCIEMACSR); + printk(KERN_NOTICE "PCI: PCIe#%d x%d link detected\n", + port->index, (data >> 20) & 0x3f); + } else + printk(KERN_NOTICE "PCI: PCIe#%d link down\n", + port->index); - for (i = 0; i < chan->nr_resources; i++) { + for (i = win = 0; i < chan->nr_resources; i++) { struct resource *res = chan->resources + i; resource_size_t size; - u32 enable_mask; + u32 mask; - pci_write_reg(chan, 0x00000000, SH4A_PCIEPTCTLR(i)); + /* + * We can't use the 32-bit mode windows in legacy 29-bit + * mode, so just skip them entirely. + */ + if ((res->flags & IORESOURCE_MEM_32BIT) && __in_29bit_mode()) + continue; - size = resource_size(res); + pci_write_reg(chan, 0x00000000, SH4A_PCIEPTCTLR(win)); /* * The PAMR mask is calculated in units of 256kB, which * keeps things pretty simple. */ - __raw_writel(((roundup_pow_of_two(size) / SZ_256K) - 1) << 18, - chan->reg_base + SH4A_PCIEPAMR(i)); + size = resource_size(res); + mask = (roundup_pow_of_two(size) / SZ_256K) - 1; + pci_write_reg(chan, mask << 18, SH4A_PCIEPAMR(win)); - pci_write_reg(chan, 0x00000000, SH4A_PCIEPARH(i)); - pci_write_reg(chan, 0x00000000, SH4A_PCIEPARL(i)); + pci_write_reg(chan, upper_32_bits(res->start), + SH4A_PCIEPARH(win)); + pci_write_reg(chan, lower_32_bits(res->start), + SH4A_PCIEPARL(win)); - enable_mask = MASK_PARE; + mask = MASK_PARE; if (res->flags & IORESOURCE_IO) - enable_mask |= MASK_SPC; + mask |= MASK_SPC; + + pci_write_reg(chan, mask, SH4A_PCIEPTCTLR(win)); - pci_write_reg(chan, enable_mask, SH4A_PCIEPTCTLR(i)); + win++; } return 0; } -int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) +int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { - return 71; + return evt2irq(0xae0); } -static int sh7786_pcie_core_init(void) +static int __init sh7786_pcie_core_init(void) { /* Return the number of ports */ return test_mode_pin(MODE_PIN12) ? 3 : 2; } -static int __devinit sh7786_pcie_init_hw(struct sh7786_pcie_port *port) +static void __init sh7786_pcie_init_hw(void *data, async_cookie_t cookie) { + struct sh7786_pcie_port *port = data; int ret; - ret = phy_init(port->hose); - if (unlikely(ret < 0)) - return ret; - /* * Check if we are configured in endpoint or root complex mode, * this is a fixed pin setting that applies to all PCIe ports. */ port->endpoint = test_mode_pin(MODE_PIN11); + /* + * Setup clocks, needed both for PHY and PCIe registers. + */ + ret = pcie_clk_init(port); + if (unlikely(ret < 0)) { + pr_err("clock initialization failed for port#%d\n", + port->index); + return; + } + + ret = phy_init(port); + if (unlikely(ret < 0)) { + pr_err("phy initialization failed for port#%d\n", + port->index); + return; + } + ret = pcie_init(port); - if (unlikely(ret < 0)) - return ret; + if (unlikely(ret < 0)) { + pr_err("core initialization failed for port#%d\n", + port->index); + return; + } - return register_pci_controller(port->hose); + /* In the interest of preserving device ordering, synchronize */ + async_synchronize_cookie(cookie); + + register_pci_controller(port->hose); } static struct sh7786_pcie_hwops sh7786_65nm_pcie_hwops __initdata = { @@ -390,9 +526,10 @@ static struct sh7786_pcie_hwops sh7786_65nm_pcie_hwops __initdata = { static int __init sh7786_pcie_init(void) { - int ret = 0, i; + struct clk *platclk; + int i; - printk(KERN_NOTICE "PCI: Starting intialization.\n"); + printk(KERN_NOTICE "PCI: Starting initialization.\n"); sh7786_pcie_hwops = &sh7786_65nm_pcie_hwops; @@ -407,6 +544,22 @@ static int __init sh7786_pcie_init(void) if (unlikely(!sh7786_pcie_ports)) return -ENOMEM; + /* + * Fetch any optional platform clock associated with this block. + * + * This is a rather nasty hack for boards with spec-mocking FPGAs + * that have a secondary set of clocks outside of the on-chip + * ones that need to be accounted for before there is any chance + * of touching the existing MSTP bits or CPG clocks. + */ + platclk = clk_get(NULL, "pcie_plat_clk"); + if (IS_ERR(platclk)) { + /* Sane hardware should probably get a WARN_ON.. */ + platclk = NULL; + } + + clk_enable(platclk); + printk(KERN_NOTICE "PCI: probing %d ports.\n", nr_ports); for (i = 0; i < nr_ports; i++) { @@ -416,11 +569,10 @@ static int __init sh7786_pcie_init(void) port->hose = sh7786_pci_channels + i; port->hose->io_map_base = port->hose->resources[0].start; - ret |= sh7786_pcie_hwops->port_init_hw(port); + async_schedule(sh7786_pcie_hwops->port_init_hw, port); } - if (unlikely(ret)) - return ret; + async_synchronize_full(); return 0; } diff --git a/arch/sh/drivers/pci/pcie-sh7786.h b/arch/sh/drivers/pci/pcie-sh7786.h index 90a6992576b..4a6ff55f759 100644 --- a/arch/sh/drivers/pci/pcie-sh7786.h +++ b/arch/sh/drivers/pci/pcie-sh7786.h @@ -55,8 +55,11 @@ #define BITS_ERRRCV (0) /* 0 ERRRCV 0 */ #define MASK_ERRRCV (1<<BITS_ERRRCV) +/* PCIEENBLR */ +#define SH4A_PCIEENBLR (0x000008) /* R/W - 0x0000 0001 32 */ + /* PCIEECR */ -#define SH4A_PCIEECR (0x000008) /* R/W - 0x0000 0000 32 */ +#define SH4A_PCIEECR (0x00000C) /* R/W - 0x0000 0000 32 */ #define BITS_ENBL (0) /* 0 ENBL 0 R/W */ #define MASK_ENBL (1<<BITS_ENBL) @@ -113,6 +116,27 @@ #define BITS_MDATA (0) #define MASK_MDATA (0xffffffff<<BITS_MDATA) +/* PCIEUNLOCKCR */ +#define SH4A_PCIEUNLOCKCR (0x000048) /* R/W - 0x0000 0000 32 */ + +/* PCIEIDR */ +#define SH4A_PCIEIDR (0x000060) /* R/W - 0x0101 1101 32 */ + +/* PCIEDBGCTLR */ +#define SH4A_PCIEDBGCTLR (0x000100) /* R/W - 0x0000 0000 32 */ + +/* PCIEINTXR */ +#define SH4A_PCIEINTXR (0x004000) /* R/W - 0x0000 0000 32 */ + +/* PCIERMSGR */ +#define SH4A_PCIERMSGR (0x004010) /* R/W - 0x0000 0000 32 */ + +/* PCIERSTR */ +#define SH4A_PCIERSTR(x) (0x008000 + ((x) * 0x4)) /* R/W - 0x0000 0000 32 */ + +/* PCIESRSTR */ +#define SH4A_PCIESRSTR (0x008040) /* R/W - 0x0000 0000 32 */ + /* PCIEPHYCTLR */ #define SH4A_PCIEPHYCTLR (0x010000) /* R/W - 0x0000 0000 32 */ #define BITS_CKE (0) @@ -152,7 +176,7 @@ #define MASK_CFINT (1<<BITS_CFINT) /* PCIETSTR */ -#define SH4A_PCIETSTR (0x020004) /* R/W R/W 0x0000 0000 32 */ +#define SH4A_PCIETSTR (0x020004) /* R 0x0000 0000 32 */ /* PCIEINTR */ #define SH4A_PCIEINTR (0x020008) /* R/W R/W 0x0000 0000 32 */ @@ -236,6 +260,9 @@ #define BITS_INTPM (8) #define MASK_INTPM (1<<BITS_INTPM) +/* PCIEEH0R */ +#define SH4A_PCIEEHR(x) (0x020010 + ((x) * 0x4)) /* R - 0x0000 0000 32 */ + /* PCIEAIR */ #define SH4A_PCIEAIR (SH4A_PCIE_BASE + 0x020010) /* R/W R/W 0xxxxx xxxx 32 */ @@ -244,6 +271,25 @@ /* PCIEERRFR */ // Rev1.18 #define SH4A_PCIEERRFR (0x020020) /* R/W R/W 0xxxxx xxxx 32 */ // Rev1.18 + +/* PCIEERRFER */ +#define SH4A_PCIEERRFER (0x020024) /* R/W R/W 0x0000 0000 32 */ + +/* PCIEERRFR2 */ +#define SH4A_PCIEERRFR2 (0x020028) /* R/W R/W 0x0000 0000 32 */ + +/* PCIEMSIR */ +#define SH4A_PCIEMSIR (0x020040) /* R/W - 0x0000 0000 32 */ + +/* PCIEMSIFR */ +#define SH4A_PCIEMSIFR (0x020044) /* R/W R/W 0x0000 0000 32 */ + +/* PCIEPWRCTLR */ +#define SH4A_PCIEPWRCTLR (0x020100) /* R/W - 0x0000 0000 32 */ + +/* PCIEPCCTLR */ +#define SH4A_PCIEPCCTLR (0x020180) /* R/W - 0x0000 0000 32 */ + // Rev1.18 /* PCIELAR0 */ #define SH4A_PCIELAR0 (0x020200) /* R/W R/W 0x0000 0000 32 */ @@ -352,6 +398,7 @@ #define SH4A_PCIEDMCCR0 (0x021120) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEDMCC2R0 (0x021124) /* R/W R/W 0x0000 0000 - */ #define SH4A_PCIEDMCCCR0 (0x021128) /* R/W R/W 0x0000 0000 32 */ +#define SH4A_PCIEDMCHSR0 (0x02112C) /* R/W - 0x0000 0000 32 */ #define SH4A_PCIEDMSAR1 (0x021140) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEDMSAHR1 (0x021144) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEDMDAR1 (0x021148) /* R/W R/W 0x0000 0000 32 */ @@ -363,6 +410,7 @@ #define SH4A_PCIEDMCCR1 (0x021160) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEDMCC2R1 (0x021164) /* R/W R/W 0x0000 0000 - */ #define SH4A_PCIEDMCCCR1 (0x021168) /* R/W R/W 0x0000 0000 32 */ +#define SH4A_PCIEDMCHSR1 (0x02116C) /* R/W - 0x0000 0000 32 */ #define SH4A_PCIEDMSAR2 (0x021180) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEDMSAHR2 (0x021184) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEDMDAR2 (0x021188) /* R/W R/W 0x0000 0000 32 */ @@ -385,6 +433,7 @@ #define SH4A_PCIEDMCCR3 (0x0211E0) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEDMCC2R3 (0x0211E4) /* R/W R/W 0x0000 0000 - */ #define SH4A_PCIEDMCCCR3 (0x0211E8) /* R/W R/W 0x0000 0000 32 */ +#define SH4A_PCIEDMCHSR3 (0x0211EC) /* R/W R/W 0x0000 0000 32 */ #define SH4A_PCIEPCICONF0 (0x040000) /* R R - 8/16/32 */ #define SH4A_PCIEPCICONF1 (0x040004) /* R/W R/W 0x0008 0000 8/16/32 */ #define SH4A_PCIEPCICONF2 (0x040008) /* R/W R/W 0xFF00 0000 8/16/32 */ diff --git a/arch/sh/drivers/push-switch.c b/arch/sh/drivers/push-switch.c index 7b42c247316..5bfb341cc5c 100644 --- a/arch/sh/drivers/push-switch.c +++ b/arch/sh/drivers/push-switch.c @@ -63,7 +63,7 @@ static int switch_drv_probe(struct platform_device *pdev) BUG_ON(!psw_info); ret = request_irq(irq, psw_info->irq_handler, - IRQF_DISABLED | psw_info->irq_flags, + psw_info->irq_flags, psw_info->name ? psw_info->name : DRV_NAME, pdev); if (unlikely(ret < 0)) goto err; @@ -107,7 +107,7 @@ static int switch_drv_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_switch); platform_set_drvdata(pdev, NULL); - flush_scheduled_work(); + flush_work(&psw->work); del_timer_sync(&psw->debounce); free_irq(irq, pdev); |
