18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci* cypress_cy7c63.c 48c2ecf20Sopenharmony_ci* 58c2ecf20Sopenharmony_ci* Copyright (c) 2006-2007 Oliver Bock (bock@tfh-berlin.de) 68c2ecf20Sopenharmony_ci* 78c2ecf20Sopenharmony_ci* This driver is based on the Cypress USB Driver by Marcus Maul 88c2ecf20Sopenharmony_ci* (cyport) and the 2.0 version of Greg Kroah-Hartman's 98c2ecf20Sopenharmony_ci* USB Skeleton driver. 108c2ecf20Sopenharmony_ci* 118c2ecf20Sopenharmony_ci* This is a generic driver for the Cypress CY7C63xxx family. 128c2ecf20Sopenharmony_ci* For the time being it enables you to read from and write to 138c2ecf20Sopenharmony_ci* the single I/O ports of the device. 148c2ecf20Sopenharmony_ci* 158c2ecf20Sopenharmony_ci* Supported vendors: AK Modul-Bus Computer GmbH 168c2ecf20Sopenharmony_ci* (Firmware "Port-Chip") 178c2ecf20Sopenharmony_ci* 188c2ecf20Sopenharmony_ci* Supported devices: CY7C63001A-PC 198c2ecf20Sopenharmony_ci* CY7C63001C-PXC 208c2ecf20Sopenharmony_ci* CY7C63001C-SXC 218c2ecf20Sopenharmony_ci* 228c2ecf20Sopenharmony_ci* Supported functions: Read/Write Ports 238c2ecf20Sopenharmony_ci* 248c2ecf20Sopenharmony_ci* 258c2ecf20Sopenharmony_ci* For up-to-date information please visit: 268c2ecf20Sopenharmony_ci* http://www.obock.de/kernel/cypress 278c2ecf20Sopenharmony_ci*/ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/module.h> 308c2ecf20Sopenharmony_ci#include <linux/kernel.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci#include <linux/usb.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Oliver Bock (bock@tfh-berlin.de)" 358c2ecf20Sopenharmony_ci#define DRIVER_DESC "Cypress CY7C63xxx USB driver" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define CYPRESS_VENDOR_ID 0xa2c 388c2ecf20Sopenharmony_ci#define CYPRESS_PRODUCT_ID 0x8 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define CYPRESS_READ_PORT 0x4 418c2ecf20Sopenharmony_ci#define CYPRESS_WRITE_PORT 0x5 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define CYPRESS_READ_RAM 0x2 448c2ecf20Sopenharmony_ci#define CYPRESS_WRITE_RAM 0x3 458c2ecf20Sopenharmony_ci#define CYPRESS_READ_ROM 0x1 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define CYPRESS_READ_PORT_ID0 0 488c2ecf20Sopenharmony_ci#define CYPRESS_WRITE_PORT_ID0 0 498c2ecf20Sopenharmony_ci#define CYPRESS_READ_PORT_ID1 0x2 508c2ecf20Sopenharmony_ci#define CYPRESS_WRITE_PORT_ID1 1 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define CYPRESS_MAX_REQSIZE 8 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* table of devices that work with this driver */ 568c2ecf20Sopenharmony_cistatic const struct usb_device_id cypress_table[] = { 578c2ecf20Sopenharmony_ci { USB_DEVICE(CYPRESS_VENDOR_ID, CYPRESS_PRODUCT_ID) }, 588c2ecf20Sopenharmony_ci { } 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, cypress_table); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* structure to hold all of our device specific stuff */ 638c2ecf20Sopenharmony_cistruct cypress { 648c2ecf20Sopenharmony_ci struct usb_device * udev; 658c2ecf20Sopenharmony_ci unsigned char port[2]; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* used to send usb control messages to device */ 698c2ecf20Sopenharmony_cistatic int vendor_command(struct cypress *dev, unsigned char request, 708c2ecf20Sopenharmony_ci unsigned char address, unsigned char data) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int retval = 0; 738c2ecf20Sopenharmony_ci unsigned int pipe; 748c2ecf20Sopenharmony_ci unsigned char *iobuf; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* allocate some memory for the i/o buffer*/ 778c2ecf20Sopenharmony_ci iobuf = kzalloc(CYPRESS_MAX_REQSIZE, GFP_KERNEL); 788c2ecf20Sopenharmony_ci if (!iobuf) { 798c2ecf20Sopenharmony_ci retval = -ENOMEM; 808c2ecf20Sopenharmony_ci goto error; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "Sending usb_control_msg (data: %d)\n", data); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* prepare usb control message and send it upstream */ 868c2ecf20Sopenharmony_ci pipe = usb_rcvctrlpipe(dev->udev, 0); 878c2ecf20Sopenharmony_ci retval = usb_control_msg(dev->udev, pipe, request, 888c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, 898c2ecf20Sopenharmony_ci address, data, iobuf, CYPRESS_MAX_REQSIZE, 908c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* store returned data (more READs to be added) */ 938c2ecf20Sopenharmony_ci switch (request) { 948c2ecf20Sopenharmony_ci case CYPRESS_READ_PORT: 958c2ecf20Sopenharmony_ci if (address == CYPRESS_READ_PORT_ID0) { 968c2ecf20Sopenharmony_ci dev->port[0] = iobuf[1]; 978c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 988c2ecf20Sopenharmony_ci "READ_PORT0 returned: %d\n", 998c2ecf20Sopenharmony_ci dev->port[0]); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci else if (address == CYPRESS_READ_PORT_ID1) { 1028c2ecf20Sopenharmony_ci dev->port[1] = iobuf[1]; 1038c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 1048c2ecf20Sopenharmony_ci "READ_PORT1 returned: %d\n", 1058c2ecf20Sopenharmony_ci dev->port[1]); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci kfree(iobuf); 1118c2ecf20Sopenharmony_cierror: 1128c2ecf20Sopenharmony_ci return retval; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* write port value */ 1168c2ecf20Sopenharmony_cistatic ssize_t write_port(struct device *dev, struct device_attribute *attr, 1178c2ecf20Sopenharmony_ci const char *buf, size_t count, 1188c2ecf20Sopenharmony_ci int port_num, int write_id) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci int value = -1; 1218c2ecf20Sopenharmony_ci int result = 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 1248c2ecf20Sopenharmony_ci struct cypress *cyp = usb_get_intfdata(intf); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci dev_dbg(&cyp->udev->dev, "WRITE_PORT%d called\n", port_num); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* validate input data */ 1298c2ecf20Sopenharmony_ci if (sscanf(buf, "%d", &value) < 1) { 1308c2ecf20Sopenharmony_ci result = -EINVAL; 1318c2ecf20Sopenharmony_ci goto error; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci if (value < 0 || value > 255) { 1348c2ecf20Sopenharmony_ci result = -EINVAL; 1358c2ecf20Sopenharmony_ci goto error; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci result = vendor_command(cyp, CYPRESS_WRITE_PORT, write_id, 1398c2ecf20Sopenharmony_ci (unsigned char)value); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result); 1428c2ecf20Sopenharmony_cierror: 1438c2ecf20Sopenharmony_ci return result < 0 ? result : count; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* attribute callback handler (write) */ 1478c2ecf20Sopenharmony_cistatic ssize_t port0_store(struct device *dev, 1488c2ecf20Sopenharmony_ci struct device_attribute *attr, 1498c2ecf20Sopenharmony_ci const char *buf, size_t count) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci return write_port(dev, attr, buf, count, 0, CYPRESS_WRITE_PORT_ID0); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* attribute callback handler (write) */ 1558c2ecf20Sopenharmony_cistatic ssize_t port1_store(struct device *dev, 1568c2ecf20Sopenharmony_ci struct device_attribute *attr, 1578c2ecf20Sopenharmony_ci const char *buf, size_t count) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return write_port(dev, attr, buf, count, 1, CYPRESS_WRITE_PORT_ID1); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* read port value */ 1638c2ecf20Sopenharmony_cistatic ssize_t read_port(struct device *dev, struct device_attribute *attr, 1648c2ecf20Sopenharmony_ci char *buf, int port_num, int read_id) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int result = 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 1698c2ecf20Sopenharmony_ci struct cypress *cyp = usb_get_intfdata(intf); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", port_num); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci result = vendor_command(cyp, CYPRESS_READ_PORT, read_id, 0); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return sprintf(buf, "%d", cyp->port[port_num]); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* attribute callback handler (read) */ 1818c2ecf20Sopenharmony_cistatic ssize_t port0_show(struct device *dev, 1828c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci return read_port(dev, attr, buf, 0, CYPRESS_READ_PORT_ID0); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(port0); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* attribute callback handler (read) */ 1898c2ecf20Sopenharmony_cistatic ssize_t port1_show(struct device *dev, 1908c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return read_port(dev, attr, buf, 1, CYPRESS_READ_PORT_ID1); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(port1); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic struct attribute *cypress_attrs[] = { 1978c2ecf20Sopenharmony_ci &dev_attr_port0.attr, 1988c2ecf20Sopenharmony_ci &dev_attr_port1.attr, 1998c2ecf20Sopenharmony_ci NULL, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(cypress); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int cypress_probe(struct usb_interface *interface, 2048c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct cypress *dev = NULL; 2078c2ecf20Sopenharmony_ci int retval = -ENOMEM; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 2108c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 2118c2ecf20Sopenharmony_ci if (!dev) 2128c2ecf20Sopenharmony_ci goto error_mem; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dev->udev = usb_get_dev(interface_to_usbdev(interface)); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* save our data pointer in this interface device */ 2178c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* let the user know that the device is now attached */ 2208c2ecf20Sopenharmony_ci dev_info(&interface->dev, 2218c2ecf20Sopenharmony_ci "Cypress CY7C63xxx device now attached\n"); 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cierror_mem: 2258c2ecf20Sopenharmony_ci return retval; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void cypress_disconnect(struct usb_interface *interface) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct cypress *dev; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dev = usb_get_intfdata(interface); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* the intfdata can be set to NULL only after the 2358c2ecf20Sopenharmony_ci * device files have been removed */ 2368c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci usb_put_dev(dev->udev); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci dev_info(&interface->dev, 2418c2ecf20Sopenharmony_ci "Cypress CY7C63xxx device now disconnected\n"); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci kfree(dev); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct usb_driver cypress_driver = { 2478c2ecf20Sopenharmony_ci .name = "cypress_cy7c63", 2488c2ecf20Sopenharmony_ci .probe = cypress_probe, 2498c2ecf20Sopenharmony_ci .disconnect = cypress_disconnect, 2508c2ecf20Sopenharmony_ci .id_table = cypress_table, 2518c2ecf20Sopenharmony_ci .dev_groups = cypress_groups, 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cimodule_usb_driver(cypress_driver); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 2578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 260