18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006, 2007 Eugene Konev <ejka@openwrt.org> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Parts of the VLYNQ specification can be found here: 68c2ecf20Sopenharmony_ci * http://www.ti.com/litv/pdf/sprue36a 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/irq.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/vlynq.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_PM_ENABLE 0x80000000 268c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_CLOCK_INT 0x00008000 278c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16) 288c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_INT_LOCAL 0x00004000 298c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_INT_ENABLE 0x00002000 308c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8) 318c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_INT2CFG 0x00000080 328c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_RESET 0x00000001 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define VLYNQ_CTRL_CLOCK_MASK (0x7 << 16) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define VLYNQ_INT_OFFSET 0x00000014 378c2ecf20Sopenharmony_ci#define VLYNQ_REMOTE_OFFSET 0x00000080 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define VLYNQ_STATUS_LINK 0x00000001 408c2ecf20Sopenharmony_ci#define VLYNQ_STATUS_LERROR 0x00000080 418c2ecf20Sopenharmony_ci#define VLYNQ_STATUS_RERROR 0x00000100 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define VINT_ENABLE 0x00000100 448c2ecf20Sopenharmony_ci#define VINT_TYPE_EDGE 0x00000080 458c2ecf20Sopenharmony_ci#define VINT_LEVEL_LOW 0x00000040 468c2ecf20Sopenharmony_ci#define VINT_VECTOR(x) ((x) & 0x1f) 478c2ecf20Sopenharmony_ci#define VINT_OFFSET(irq) (8 * ((irq) % 4)) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define VLYNQ_AUTONEGO_V2 0x00010000 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct vlynq_regs { 528c2ecf20Sopenharmony_ci u32 revision; 538c2ecf20Sopenharmony_ci u32 control; 548c2ecf20Sopenharmony_ci u32 status; 558c2ecf20Sopenharmony_ci u32 int_prio; 568c2ecf20Sopenharmony_ci u32 int_status; 578c2ecf20Sopenharmony_ci u32 int_pending; 588c2ecf20Sopenharmony_ci u32 int_ptr; 598c2ecf20Sopenharmony_ci u32 tx_offset; 608c2ecf20Sopenharmony_ci struct vlynq_mapping rx_mapping[4]; 618c2ecf20Sopenharmony_ci u32 chip; 628c2ecf20Sopenharmony_ci u32 autonego; 638c2ecf20Sopenharmony_ci u32 unused[6]; 648c2ecf20Sopenharmony_ci u32 int_device[8]; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#ifdef CONFIG_VLYNQ_DEBUG 688c2ecf20Sopenharmony_cistatic void vlynq_dump_regs(struct vlynq_device *dev) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int i; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n", 738c2ecf20Sopenharmony_ci dev->local, dev->remote); 748c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 758c2ecf20Sopenharmony_ci printk(KERN_DEBUG "VLYNQ: local %d: %08x\n", 768c2ecf20Sopenharmony_ci i + 1, ((u32 *)dev->local)[i]); 778c2ecf20Sopenharmony_ci printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n", 788c2ecf20Sopenharmony_ci i + 1, ((u32 *)dev->remote)[i]); 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void vlynq_dump_mem(u32 *base, int count) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci for (i = 0; i < (count + 3) / 4; i++) { 878c2ecf20Sopenharmony_ci if (i % 4 == 0) 888c2ecf20Sopenharmony_ci printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4); 898c2ecf20Sopenharmony_ci printk(KERN_DEBUG " 0x%08x", *(base + i)); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci printk(KERN_DEBUG "\n"); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci#endif 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Check the VLYNQ link status with a given device */ 968c2ecf20Sopenharmony_cistatic int vlynq_linked(struct vlynq_device *dev) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int i; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) 1018c2ecf20Sopenharmony_ci if (readl(&dev->local->status) & VLYNQ_STATUS_LINK) 1028c2ecf20Sopenharmony_ci return 1; 1038c2ecf20Sopenharmony_ci else 1048c2ecf20Sopenharmony_ci cpu_relax(); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void vlynq_reset(struct vlynq_device *dev) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci writel(readl(&dev->local->control) | VLYNQ_CTRL_RESET, 1128c2ecf20Sopenharmony_ci &dev->local->control); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Wait for the devices to finish resetting */ 1158c2ecf20Sopenharmony_ci msleep(5); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Remove reset bit */ 1188c2ecf20Sopenharmony_ci writel(readl(&dev->local->control) & ~VLYNQ_CTRL_RESET, 1198c2ecf20Sopenharmony_ci &dev->local->control); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Give some time for the devices to settle */ 1228c2ecf20Sopenharmony_ci msleep(5); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void vlynq_irq_unmask(struct irq_data *d) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct vlynq_device *dev = irq_data_get_irq_chip_data(d); 1288c2ecf20Sopenharmony_ci int virq; 1298c2ecf20Sopenharmony_ci u32 val; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci BUG_ON(!dev); 1328c2ecf20Sopenharmony_ci virq = d->irq - dev->irq_start; 1338c2ecf20Sopenharmony_ci val = readl(&dev->remote->int_device[virq >> 2]); 1348c2ecf20Sopenharmony_ci val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq); 1358c2ecf20Sopenharmony_ci writel(val, &dev->remote->int_device[virq >> 2]); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void vlynq_irq_mask(struct irq_data *d) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct vlynq_device *dev = irq_data_get_irq_chip_data(d); 1418c2ecf20Sopenharmony_ci int virq; 1428c2ecf20Sopenharmony_ci u32 val; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci BUG_ON(!dev); 1458c2ecf20Sopenharmony_ci virq = d->irq - dev->irq_start; 1468c2ecf20Sopenharmony_ci val = readl(&dev->remote->int_device[virq >> 2]); 1478c2ecf20Sopenharmony_ci val &= ~(VINT_ENABLE << VINT_OFFSET(virq)); 1488c2ecf20Sopenharmony_ci writel(val, &dev->remote->int_device[virq >> 2]); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int vlynq_irq_type(struct irq_data *d, unsigned int flow_type) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct vlynq_device *dev = irq_data_get_irq_chip_data(d); 1548c2ecf20Sopenharmony_ci int virq; 1558c2ecf20Sopenharmony_ci u32 val; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci BUG_ON(!dev); 1588c2ecf20Sopenharmony_ci virq = d->irq - dev->irq_start; 1598c2ecf20Sopenharmony_ci val = readl(&dev->remote->int_device[virq >> 2]); 1608c2ecf20Sopenharmony_ci switch (flow_type & IRQ_TYPE_SENSE_MASK) { 1618c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 1628c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 1638c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 1648c2ecf20Sopenharmony_ci val |= VINT_TYPE_EDGE << VINT_OFFSET(virq); 1658c2ecf20Sopenharmony_ci val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 1688c2ecf20Sopenharmony_ci val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); 1698c2ecf20Sopenharmony_ci val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 1728c2ecf20Sopenharmony_ci val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); 1738c2ecf20Sopenharmony_ci val |= VINT_LEVEL_LOW << VINT_OFFSET(virq); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci default: 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci writel(val, &dev->remote->int_device[virq >> 2]); 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void vlynq_local_ack(struct irq_data *d) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct vlynq_device *dev = irq_data_get_irq_chip_data(d); 1858c2ecf20Sopenharmony_ci u32 status = readl(&dev->local->status); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci pr_debug("%s: local status: 0x%08x\n", 1888c2ecf20Sopenharmony_ci dev_name(&dev->dev), status); 1898c2ecf20Sopenharmony_ci writel(status, &dev->local->status); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void vlynq_remote_ack(struct irq_data *d) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct vlynq_device *dev = irq_data_get_irq_chip_data(d); 1958c2ecf20Sopenharmony_ci u32 status = readl(&dev->remote->status); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci pr_debug("%s: remote status: 0x%08x\n", 1988c2ecf20Sopenharmony_ci dev_name(&dev->dev), status); 1998c2ecf20Sopenharmony_ci writel(status, &dev->remote->status); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic irqreturn_t vlynq_irq(int irq, void *dev_id) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct vlynq_device *dev = dev_id; 2058c2ecf20Sopenharmony_ci u32 status; 2068c2ecf20Sopenharmony_ci int virq = 0; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci status = readl(&dev->local->int_status); 2098c2ecf20Sopenharmony_ci writel(status, &dev->local->int_status); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (unlikely(!status)) 2128c2ecf20Sopenharmony_ci spurious_interrupt(); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci while (status) { 2158c2ecf20Sopenharmony_ci if (status & 1) 2168c2ecf20Sopenharmony_ci do_IRQ(dev->irq_start + virq); 2178c2ecf20Sopenharmony_ci status >>= 1; 2188c2ecf20Sopenharmony_ci virq++; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic struct irq_chip vlynq_irq_chip = { 2258c2ecf20Sopenharmony_ci .name = "vlynq", 2268c2ecf20Sopenharmony_ci .irq_unmask = vlynq_irq_unmask, 2278c2ecf20Sopenharmony_ci .irq_mask = vlynq_irq_mask, 2288c2ecf20Sopenharmony_ci .irq_set_type = vlynq_irq_type, 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic struct irq_chip vlynq_local_chip = { 2328c2ecf20Sopenharmony_ci .name = "vlynq local error", 2338c2ecf20Sopenharmony_ci .irq_unmask = vlynq_irq_unmask, 2348c2ecf20Sopenharmony_ci .irq_mask = vlynq_irq_mask, 2358c2ecf20Sopenharmony_ci .irq_ack = vlynq_local_ack, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct irq_chip vlynq_remote_chip = { 2398c2ecf20Sopenharmony_ci .name = "vlynq local error", 2408c2ecf20Sopenharmony_ci .irq_unmask = vlynq_irq_unmask, 2418c2ecf20Sopenharmony_ci .irq_mask = vlynq_irq_mask, 2428c2ecf20Sopenharmony_ci .irq_ack = vlynq_remote_ack, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int vlynq_setup_irq(struct vlynq_device *dev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci u32 val; 2488c2ecf20Sopenharmony_ci int i, virq; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (dev->local_irq == dev->remote_irq) { 2518c2ecf20Sopenharmony_ci printk(KERN_ERR 2528c2ecf20Sopenharmony_ci "%s: local vlynq irq should be different from remote\n", 2538c2ecf20Sopenharmony_ci dev_name(&dev->dev)); 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Clear local and remote error bits */ 2588c2ecf20Sopenharmony_ci writel(readl(&dev->local->status), &dev->local->status); 2598c2ecf20Sopenharmony_ci writel(readl(&dev->remote->status), &dev->remote->status); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Now setup interrupts */ 2628c2ecf20Sopenharmony_ci val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq); 2638c2ecf20Sopenharmony_ci val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL | 2648c2ecf20Sopenharmony_ci VLYNQ_CTRL_INT2CFG; 2658c2ecf20Sopenharmony_ci val |= readl(&dev->local->control); 2668c2ecf20Sopenharmony_ci writel(VLYNQ_INT_OFFSET, &dev->local->int_ptr); 2678c2ecf20Sopenharmony_ci writel(val, &dev->local->control); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq); 2708c2ecf20Sopenharmony_ci val |= VLYNQ_CTRL_INT_ENABLE; 2718c2ecf20Sopenharmony_ci val |= readl(&dev->remote->control); 2728c2ecf20Sopenharmony_ci writel(VLYNQ_INT_OFFSET, &dev->remote->int_ptr); 2738c2ecf20Sopenharmony_ci writel(val, &dev->remote->int_ptr); 2748c2ecf20Sopenharmony_ci writel(val, &dev->remote->control); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci for (i = dev->irq_start; i <= dev->irq_end; i++) { 2778c2ecf20Sopenharmony_ci virq = i - dev->irq_start; 2788c2ecf20Sopenharmony_ci if (virq == dev->local_irq) { 2798c2ecf20Sopenharmony_ci irq_set_chip_and_handler(i, &vlynq_local_chip, 2808c2ecf20Sopenharmony_ci handle_level_irq); 2818c2ecf20Sopenharmony_ci irq_set_chip_data(i, dev); 2828c2ecf20Sopenharmony_ci } else if (virq == dev->remote_irq) { 2838c2ecf20Sopenharmony_ci irq_set_chip_and_handler(i, &vlynq_remote_chip, 2848c2ecf20Sopenharmony_ci handle_level_irq); 2858c2ecf20Sopenharmony_ci irq_set_chip_data(i, dev); 2868c2ecf20Sopenharmony_ci } else { 2878c2ecf20Sopenharmony_ci irq_set_chip_and_handler(i, &vlynq_irq_chip, 2888c2ecf20Sopenharmony_ci handle_simple_irq); 2898c2ecf20Sopenharmony_ci irq_set_chip_data(i, dev); 2908c2ecf20Sopenharmony_ci writel(0, &dev->remote->int_device[virq >> 2]); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) { 2958c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: request_irq failed\n", 2968c2ecf20Sopenharmony_ci dev_name(&dev->dev)); 2978c2ecf20Sopenharmony_ci return -EAGAIN; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void vlynq_device_release(struct device *dev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct vlynq_device *vdev = to_vlynq_device(dev); 3068c2ecf20Sopenharmony_ci kfree(vdev); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int vlynq_device_match(struct device *dev, 3108c2ecf20Sopenharmony_ci struct device_driver *drv) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct vlynq_device *vdev = to_vlynq_device(dev); 3138c2ecf20Sopenharmony_ci struct vlynq_driver *vdrv = to_vlynq_driver(drv); 3148c2ecf20Sopenharmony_ci struct vlynq_device_id *ids = vdrv->id_table; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci while (ids->id) { 3178c2ecf20Sopenharmony_ci if (ids->id == vdev->dev_id) { 3188c2ecf20Sopenharmony_ci vdev->divisor = ids->divisor; 3198c2ecf20Sopenharmony_ci vlynq_set_drvdata(vdev, ids); 3208c2ecf20Sopenharmony_ci printk(KERN_INFO "Driver found for VLYNQ " 3218c2ecf20Sopenharmony_ci "device: %08x\n", vdev->dev_id); 3228c2ecf20Sopenharmony_ci return 1; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Not using the %08x VLYNQ device's driver" 3258c2ecf20Sopenharmony_ci " for VLYNQ device: %08x\n", ids->id, vdev->dev_id); 3268c2ecf20Sopenharmony_ci ids++; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int vlynq_device_probe(struct device *dev) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct vlynq_device *vdev = to_vlynq_device(dev); 3348c2ecf20Sopenharmony_ci struct vlynq_driver *drv = to_vlynq_driver(dev->driver); 3358c2ecf20Sopenharmony_ci struct vlynq_device_id *id = vlynq_get_drvdata(vdev); 3368c2ecf20Sopenharmony_ci int result = -ENODEV; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (drv->probe) 3398c2ecf20Sopenharmony_ci result = drv->probe(vdev, id); 3408c2ecf20Sopenharmony_ci if (result) 3418c2ecf20Sopenharmony_ci put_device(dev); 3428c2ecf20Sopenharmony_ci return result; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int vlynq_device_remove(struct device *dev) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct vlynq_driver *drv = to_vlynq_driver(dev->driver); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (drv->remove) 3508c2ecf20Sopenharmony_ci drv->remove(to_vlynq_device(dev)); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciint __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci driver->driver.name = driver->name; 3588c2ecf20Sopenharmony_ci driver->driver.bus = &vlynq_bus_type; 3598c2ecf20Sopenharmony_ci return driver_register(&driver->driver); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__vlynq_register_driver); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_civoid vlynq_unregister_driver(struct vlynq_driver *driver) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci driver_unregister(&driver->driver); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_unregister_driver); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* 3708c2ecf20Sopenharmony_ci * A VLYNQ remote device can clock the VLYNQ bus master 3718c2ecf20Sopenharmony_ci * using a dedicated clock line. In that case, both the 3728c2ecf20Sopenharmony_ci * remove device and the bus master should have the same 3738c2ecf20Sopenharmony_ci * serial clock dividers configured. Iterate through the 3748c2ecf20Sopenharmony_ci * 8 possible dividers until we actually link with the 3758c2ecf20Sopenharmony_ci * device. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_cistatic int __vlynq_try_remote(struct vlynq_device *dev) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci int i; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci vlynq_reset(dev); 3828c2ecf20Sopenharmony_ci for (i = dev->dev_id ? vlynq_rdiv2 : vlynq_rdiv8; dev->dev_id ? 3838c2ecf20Sopenharmony_ci i <= vlynq_rdiv8 : i >= vlynq_rdiv2; 3848c2ecf20Sopenharmony_ci dev->dev_id ? i++ : i--) { 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!vlynq_linked(dev)) 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci writel((readl(&dev->remote->control) & 3908c2ecf20Sopenharmony_ci ~VLYNQ_CTRL_CLOCK_MASK) | 3918c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_INT | 3928c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1), 3938c2ecf20Sopenharmony_ci &dev->remote->control); 3948c2ecf20Sopenharmony_ci writel((readl(&dev->local->control) 3958c2ecf20Sopenharmony_ci & ~(VLYNQ_CTRL_CLOCK_INT | 3968c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_MASK)) | 3978c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1), 3988c2ecf20Sopenharmony_ci &dev->local->control); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (vlynq_linked(dev)) { 4018c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4028c2ecf20Sopenharmony_ci "%s: using remote clock divisor %d\n", 4038c2ecf20Sopenharmony_ci dev_name(&dev->dev), i - vlynq_rdiv1 + 1); 4048c2ecf20Sopenharmony_ci dev->divisor = i; 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci } else { 4078c2ecf20Sopenharmony_ci vlynq_reset(dev); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return -ENODEV; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* 4158c2ecf20Sopenharmony_ci * A VLYNQ remote device can be clocked by the VLYNQ bus 4168c2ecf20Sopenharmony_ci * master using a dedicated clock line. In that case, only 4178c2ecf20Sopenharmony_ci * the bus master configures the serial clock divider. 4188c2ecf20Sopenharmony_ci * Iterate through the 8 possible dividers until we 4198c2ecf20Sopenharmony_ci * actually get a link with the device. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistatic int __vlynq_try_local(struct vlynq_device *dev) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci int i; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci vlynq_reset(dev); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci for (i = dev->dev_id ? vlynq_ldiv2 : vlynq_ldiv8; dev->dev_id ? 4288c2ecf20Sopenharmony_ci i <= vlynq_ldiv8 : i >= vlynq_ldiv2; 4298c2ecf20Sopenharmony_ci dev->dev_id ? i++ : i--) { 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci writel((readl(&dev->local->control) & 4328c2ecf20Sopenharmony_ci ~VLYNQ_CTRL_CLOCK_MASK) | 4338c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_INT | 4348c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1), 4358c2ecf20Sopenharmony_ci &dev->local->control); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (vlynq_linked(dev)) { 4388c2ecf20Sopenharmony_ci printk(KERN_DEBUG 4398c2ecf20Sopenharmony_ci "%s: using local clock divisor %d\n", 4408c2ecf20Sopenharmony_ci dev_name(&dev->dev), i - vlynq_ldiv1 + 1); 4418c2ecf20Sopenharmony_ci dev->divisor = i; 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci } else { 4448c2ecf20Sopenharmony_ci vlynq_reset(dev); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return -ENODEV; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/* 4528c2ecf20Sopenharmony_ci * When using external clocking method, serial clock 4538c2ecf20Sopenharmony_ci * is supplied by an external oscillator, therefore we 4548c2ecf20Sopenharmony_ci * should mask the local clock bit in the clock control 4558c2ecf20Sopenharmony_ci * register for both the bus master and the remote device. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_cistatic int __vlynq_try_external(struct vlynq_device *dev) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci vlynq_reset(dev); 4608c2ecf20Sopenharmony_ci if (!vlynq_linked(dev)) 4618c2ecf20Sopenharmony_ci return -ENODEV; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci writel((readl(&dev->remote->control) & 4648c2ecf20Sopenharmony_ci ~VLYNQ_CTRL_CLOCK_INT), 4658c2ecf20Sopenharmony_ci &dev->remote->control); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci writel((readl(&dev->local->control) & 4688c2ecf20Sopenharmony_ci ~VLYNQ_CTRL_CLOCK_INT), 4698c2ecf20Sopenharmony_ci &dev->local->control); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (vlynq_linked(dev)) { 4728c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: using external clock\n", 4738c2ecf20Sopenharmony_ci dev_name(&dev->dev)); 4748c2ecf20Sopenharmony_ci dev->divisor = vlynq_div_external; 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return -ENODEV; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int __vlynq_enable_device(struct vlynq_device *dev) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci int result; 4848c2ecf20Sopenharmony_ci struct plat_vlynq_ops *ops = dev->dev.platform_data; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci result = ops->on(dev); 4878c2ecf20Sopenharmony_ci if (result) 4888c2ecf20Sopenharmony_ci return result; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci switch (dev->divisor) { 4918c2ecf20Sopenharmony_ci case vlynq_div_external: 4928c2ecf20Sopenharmony_ci case vlynq_div_auto: 4938c2ecf20Sopenharmony_ci /* When the device is brought from reset it should have clock 4948c2ecf20Sopenharmony_ci * generation negotiated by hardware. 4958c2ecf20Sopenharmony_ci * Check which device is generating clocks and perform setup 4968c2ecf20Sopenharmony_ci * accordingly */ 4978c2ecf20Sopenharmony_ci if (vlynq_linked(dev) && readl(&dev->remote->control) & 4988c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_INT) { 4998c2ecf20Sopenharmony_ci if (!__vlynq_try_remote(dev) || 5008c2ecf20Sopenharmony_ci !__vlynq_try_local(dev) || 5018c2ecf20Sopenharmony_ci !__vlynq_try_external(dev)) 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci } else { 5048c2ecf20Sopenharmony_ci if (!__vlynq_try_external(dev) || 5058c2ecf20Sopenharmony_ci !__vlynq_try_local(dev) || 5068c2ecf20Sopenharmony_ci !__vlynq_try_remote(dev)) 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case vlynq_ldiv1: 5118c2ecf20Sopenharmony_ci case vlynq_ldiv2: 5128c2ecf20Sopenharmony_ci case vlynq_ldiv3: 5138c2ecf20Sopenharmony_ci case vlynq_ldiv4: 5148c2ecf20Sopenharmony_ci case vlynq_ldiv5: 5158c2ecf20Sopenharmony_ci case vlynq_ldiv6: 5168c2ecf20Sopenharmony_ci case vlynq_ldiv7: 5178c2ecf20Sopenharmony_ci case vlynq_ldiv8: 5188c2ecf20Sopenharmony_ci writel(VLYNQ_CTRL_CLOCK_INT | 5198c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_DIV(dev->divisor - 5208c2ecf20Sopenharmony_ci vlynq_ldiv1), &dev->local->control); 5218c2ecf20Sopenharmony_ci writel(0, &dev->remote->control); 5228c2ecf20Sopenharmony_ci if (vlynq_linked(dev)) { 5238c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5248c2ecf20Sopenharmony_ci "%s: using local clock divisor %d\n", 5258c2ecf20Sopenharmony_ci dev_name(&dev->dev), 5268c2ecf20Sopenharmony_ci dev->divisor - vlynq_ldiv1 + 1); 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci case vlynq_rdiv1: 5318c2ecf20Sopenharmony_ci case vlynq_rdiv2: 5328c2ecf20Sopenharmony_ci case vlynq_rdiv3: 5338c2ecf20Sopenharmony_ci case vlynq_rdiv4: 5348c2ecf20Sopenharmony_ci case vlynq_rdiv5: 5358c2ecf20Sopenharmony_ci case vlynq_rdiv6: 5368c2ecf20Sopenharmony_ci case vlynq_rdiv7: 5378c2ecf20Sopenharmony_ci case vlynq_rdiv8: 5388c2ecf20Sopenharmony_ci writel(0, &dev->local->control); 5398c2ecf20Sopenharmony_ci writel(VLYNQ_CTRL_CLOCK_INT | 5408c2ecf20Sopenharmony_ci VLYNQ_CTRL_CLOCK_DIV(dev->divisor - 5418c2ecf20Sopenharmony_ci vlynq_rdiv1), &dev->remote->control); 5428c2ecf20Sopenharmony_ci if (vlynq_linked(dev)) { 5438c2ecf20Sopenharmony_ci printk(KERN_DEBUG 5448c2ecf20Sopenharmony_ci "%s: using remote clock divisor %d\n", 5458c2ecf20Sopenharmony_ci dev_name(&dev->dev), 5468c2ecf20Sopenharmony_ci dev->divisor - vlynq_rdiv1 + 1); 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ops->off(dev); 5538c2ecf20Sopenharmony_ci return -ENODEV; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ciint vlynq_enable_device(struct vlynq_device *dev) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct plat_vlynq_ops *ops = dev->dev.platform_data; 5598c2ecf20Sopenharmony_ci int result = -ENODEV; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci result = __vlynq_enable_device(dev); 5628c2ecf20Sopenharmony_ci if (result) 5638c2ecf20Sopenharmony_ci return result; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci result = vlynq_setup_irq(dev); 5668c2ecf20Sopenharmony_ci if (result) 5678c2ecf20Sopenharmony_ci ops->off(dev); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci dev->enabled = !result; 5708c2ecf20Sopenharmony_ci return result; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_enable_device); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_civoid vlynq_disable_device(struct vlynq_device *dev) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct plat_vlynq_ops *ops = dev->dev.platform_data; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci dev->enabled = 0; 5808c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 5818c2ecf20Sopenharmony_ci ops->off(dev); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_disable_device); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ciint vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, 5868c2ecf20Sopenharmony_ci struct vlynq_mapping *mapping) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci int i; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (!dev->enabled) 5918c2ecf20Sopenharmony_ci return -ENXIO; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci writel(tx_offset, &dev->local->tx_offset); 5948c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 5958c2ecf20Sopenharmony_ci writel(mapping[i].offset, &dev->local->rx_mapping[i].offset); 5968c2ecf20Sopenharmony_ci writel(mapping[i].size, &dev->local->rx_mapping[i].size); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_set_local_mapping); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ciint vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, 6038c2ecf20Sopenharmony_ci struct vlynq_mapping *mapping) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int i; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (!dev->enabled) 6088c2ecf20Sopenharmony_ci return -ENXIO; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci writel(tx_offset, &dev->remote->tx_offset); 6118c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 6128c2ecf20Sopenharmony_ci writel(mapping[i].offset, &dev->remote->rx_mapping[i].offset); 6138c2ecf20Sopenharmony_ci writel(mapping[i].size, &dev->remote->rx_mapping[i].size); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_set_remote_mapping); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ciint vlynq_set_local_irq(struct vlynq_device *dev, int virq) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci int irq = dev->irq_start + virq; 6228c2ecf20Sopenharmony_ci if (dev->enabled) 6238c2ecf20Sopenharmony_ci return -EBUSY; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if ((irq < dev->irq_start) || (irq > dev->irq_end)) 6268c2ecf20Sopenharmony_ci return -EINVAL; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (virq == dev->remote_irq) 6298c2ecf20Sopenharmony_ci return -EINVAL; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci dev->local_irq = virq; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return 0; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_set_local_irq); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ciint vlynq_set_remote_irq(struct vlynq_device *dev, int virq) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci int irq = dev->irq_start + virq; 6408c2ecf20Sopenharmony_ci if (dev->enabled) 6418c2ecf20Sopenharmony_ci return -EBUSY; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if ((irq < dev->irq_start) || (irq > dev->irq_end)) 6448c2ecf20Sopenharmony_ci return -EINVAL; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (virq == dev->local_irq) 6478c2ecf20Sopenharmony_ci return -EINVAL; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci dev->remote_irq = virq; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_set_remote_irq); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int vlynq_probe(struct platform_device *pdev) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct vlynq_device *dev; 6588c2ecf20Sopenharmony_ci struct resource *regs_res, *mem_res, *irq_res; 6598c2ecf20Sopenharmony_ci int len, result; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); 6628c2ecf20Sopenharmony_ci if (!regs_res) 6638c2ecf20Sopenharmony_ci return -ENODEV; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); 6668c2ecf20Sopenharmony_ci if (!mem_res) 6678c2ecf20Sopenharmony_ci return -ENODEV; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq"); 6708c2ecf20Sopenharmony_ci if (!irq_res) 6718c2ecf20Sopenharmony_ci return -ENODEV; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 6748c2ecf20Sopenharmony_ci if (!dev) { 6758c2ecf20Sopenharmony_ci printk(KERN_ERR 6768c2ecf20Sopenharmony_ci "vlynq: failed to allocate device structure\n"); 6778c2ecf20Sopenharmony_ci return -ENOMEM; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dev->id = pdev->id; 6818c2ecf20Sopenharmony_ci dev->dev.bus = &vlynq_bus_type; 6828c2ecf20Sopenharmony_ci dev->dev.parent = &pdev->dev; 6838c2ecf20Sopenharmony_ci dev_set_name(&dev->dev, "vlynq%d", dev->id); 6848c2ecf20Sopenharmony_ci dev->dev.platform_data = pdev->dev.platform_data; 6858c2ecf20Sopenharmony_ci dev->dev.release = vlynq_device_release; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci dev->regs_start = regs_res->start; 6888c2ecf20Sopenharmony_ci dev->regs_end = regs_res->end; 6898c2ecf20Sopenharmony_ci dev->mem_start = mem_res->start; 6908c2ecf20Sopenharmony_ci dev->mem_end = mem_res->end; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci len = resource_size(regs_res); 6938c2ecf20Sopenharmony_ci if (!request_mem_region(regs_res->start, len, dev_name(&dev->dev))) { 6948c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Can't request vlynq registers\n", 6958c2ecf20Sopenharmony_ci dev_name(&dev->dev)); 6968c2ecf20Sopenharmony_ci result = -ENXIO; 6978c2ecf20Sopenharmony_ci goto fail_request; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci dev->local = ioremap(regs_res->start, len); 7018c2ecf20Sopenharmony_ci if (!dev->local) { 7028c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Can't remap vlynq registers\n", 7038c2ecf20Sopenharmony_ci dev_name(&dev->dev)); 7048c2ecf20Sopenharmony_ci result = -ENXIO; 7058c2ecf20Sopenharmony_ci goto fail_remap; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci dev->remote = (struct vlynq_regs *)((void *)dev->local + 7098c2ecf20Sopenharmony_ci VLYNQ_REMOTE_OFFSET); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci dev->irq = platform_get_irq_byname(pdev, "irq"); 7128c2ecf20Sopenharmony_ci dev->irq_start = irq_res->start; 7138c2ecf20Sopenharmony_ci dev->irq_end = irq_res->end; 7148c2ecf20Sopenharmony_ci dev->local_irq = dev->irq_end - dev->irq_start; 7158c2ecf20Sopenharmony_ci dev->remote_irq = dev->local_irq - 1; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (device_register(&dev->dev)) 7188c2ecf20Sopenharmony_ci goto fail_register; 7198c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n", 7228c2ecf20Sopenharmony_ci dev_name(&dev->dev), (void *)dev->regs_start, dev->irq, 7238c2ecf20Sopenharmony_ci (void *)dev->mem_start); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci dev->dev_id = 0; 7268c2ecf20Sopenharmony_ci dev->divisor = vlynq_div_auto; 7278c2ecf20Sopenharmony_ci result = __vlynq_enable_device(dev); 7288c2ecf20Sopenharmony_ci if (result == 0) { 7298c2ecf20Sopenharmony_ci dev->dev_id = readl(&dev->remote->chip); 7308c2ecf20Sopenharmony_ci ((struct plat_vlynq_ops *)(dev->dev.platform_data))->off(dev); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci if (dev->dev_id) 7338c2ecf20Sopenharmony_ci printk(KERN_INFO "Found a VLYNQ device: %08x\n", dev->dev_id); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cifail_register: 7388c2ecf20Sopenharmony_ci iounmap(dev->local); 7398c2ecf20Sopenharmony_cifail_remap: 7408c2ecf20Sopenharmony_cifail_request: 7418c2ecf20Sopenharmony_ci release_mem_region(regs_res->start, len); 7428c2ecf20Sopenharmony_ci kfree(dev); 7438c2ecf20Sopenharmony_ci return result; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int vlynq_remove(struct platform_device *pdev) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct vlynq_device *dev = platform_get_drvdata(pdev); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci device_unregister(&dev->dev); 7518c2ecf20Sopenharmony_ci iounmap(dev->local); 7528c2ecf20Sopenharmony_ci release_mem_region(dev->regs_start, 7538c2ecf20Sopenharmony_ci dev->regs_end - dev->regs_start + 1); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci kfree(dev); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return 0; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic struct platform_driver vlynq_platform_driver = { 7618c2ecf20Sopenharmony_ci .driver.name = "vlynq", 7628c2ecf20Sopenharmony_ci .probe = vlynq_probe, 7638c2ecf20Sopenharmony_ci .remove = vlynq_remove, 7648c2ecf20Sopenharmony_ci}; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistruct bus_type vlynq_bus_type = { 7678c2ecf20Sopenharmony_ci .name = "vlynq", 7688c2ecf20Sopenharmony_ci .match = vlynq_device_match, 7698c2ecf20Sopenharmony_ci .probe = vlynq_device_probe, 7708c2ecf20Sopenharmony_ci .remove = vlynq_device_remove, 7718c2ecf20Sopenharmony_ci}; 7728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlynq_bus_type); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic int vlynq_init(void) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci int res = 0; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci res = bus_register(&vlynq_bus_type); 7798c2ecf20Sopenharmony_ci if (res) 7808c2ecf20Sopenharmony_ci goto fail_bus; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci res = platform_driver_register(&vlynq_platform_driver); 7838c2ecf20Sopenharmony_ci if (res) 7848c2ecf20Sopenharmony_ci goto fail_platform; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cifail_platform: 7898c2ecf20Sopenharmony_ci bus_unregister(&vlynq_bus_type); 7908c2ecf20Sopenharmony_cifail_bus: 7918c2ecf20Sopenharmony_ci return res; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic void vlynq_exit(void) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci platform_driver_unregister(&vlynq_platform_driver); 7978c2ecf20Sopenharmony_ci bus_unregister(&vlynq_bus_type); 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cimodule_init(vlynq_init); 8018c2ecf20Sopenharmony_cimodule_exit(vlynq_exit); 802