18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Generic IXP4xx beeper driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Tower Technologies 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * based on nslu2-io.c 88c2ecf20Sopenharmony_ci * Copyright (C) 2004 Karen Spearel 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Author: Alessandro Zummo <a.zummo@towertech.it> 118c2ecf20Sopenharmony_ci * Maintainers: http://www.nslu2-linux.org/ 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/input.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio.h> 208c2ecf20Sopenharmony_ci#include <mach/hardware.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ixp4xx beeper driver"); 248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 258c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ixp4xx-beeper"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(beep_lock); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int ixp4xx_timer2_irq; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void ixp4xx_spkr_control(unsigned int pin, unsigned int count) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci unsigned long flags; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci spin_lock_irqsave(&beep_lock, flags); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (count) { 388c2ecf20Sopenharmony_ci gpio_direction_output(pin, 0); 398c2ecf20Sopenharmony_ci *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; 408c2ecf20Sopenharmony_ci } else { 418c2ecf20Sopenharmony_ci gpio_direction_output(pin, 1); 428c2ecf20Sopenharmony_ci gpio_direction_input(pin); 438c2ecf20Sopenharmony_ci *IXP4XX_OSRT2 = 0; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&beep_lock, flags); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci unsigned int pin = (unsigned int) input_get_drvdata(dev); 528c2ecf20Sopenharmony_ci unsigned int count = 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (type != EV_SND) 558c2ecf20Sopenharmony_ci return -1; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci switch (code) { 588c2ecf20Sopenharmony_ci case SND_BELL: 598c2ecf20Sopenharmony_ci if (value) 608c2ecf20Sopenharmony_ci value = 1000; 618c2ecf20Sopenharmony_ci case SND_TONE: 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci default: 648c2ecf20Sopenharmony_ci return -1; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (value > 20 && value < 32767) 688c2ecf20Sopenharmony_ci count = (ixp4xx_timer_freq / (value * 4)) - 1; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ixp4xx_spkr_control(pin, count); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci unsigned int pin = (unsigned int) dev_id; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* clear interrupt */ 808c2ecf20Sopenharmony_ci *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* flip the beeper output */ 838c2ecf20Sopenharmony_ci gpio_set_value(pin, !gpio_get_value(pin)); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return IRQ_HANDLED; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int ixp4xx_spkr_probe(struct platform_device *dev) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct input_dev *input_dev; 918c2ecf20Sopenharmony_ci int irq; 928c2ecf20Sopenharmony_ci int err; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 958c2ecf20Sopenharmony_ci if (!input_dev) 968c2ecf20Sopenharmony_ci return -ENOMEM; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, (void *) dev->id); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci input_dev->name = "ixp4xx beeper", 1018c2ecf20Sopenharmony_ci input_dev->phys = "ixp4xx/gpio"; 1028c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_HOST; 1038c2ecf20Sopenharmony_ci input_dev->id.vendor = 0x001f; 1048c2ecf20Sopenharmony_ci input_dev->id.product = 0x0001; 1058c2ecf20Sopenharmony_ci input_dev->id.version = 0x0100; 1068c2ecf20Sopenharmony_ci input_dev->dev.parent = &dev->dev; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_SND); 1098c2ecf20Sopenharmony_ci input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); 1108c2ecf20Sopenharmony_ci input_dev->event = ixp4xx_spkr_event; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci irq = platform_get_irq(dev, 0); 1138c2ecf20Sopenharmony_ci if (irq < 0) { 1148c2ecf20Sopenharmony_ci err = irq; 1158c2ecf20Sopenharmony_ci goto err_free_device; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err = gpio_request(dev->id, "ixp4-beeper"); 1198c2ecf20Sopenharmony_ci if (err) 1208c2ecf20Sopenharmony_ci goto err_free_device; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci err = request_irq(irq, &ixp4xx_spkr_interrupt, 1238c2ecf20Sopenharmony_ci IRQF_NO_SUSPEND, "ixp4xx-beeper", 1248c2ecf20Sopenharmony_ci (void *) dev->id); 1258c2ecf20Sopenharmony_ci if (err) 1268c2ecf20Sopenharmony_ci goto err_free_gpio; 1278c2ecf20Sopenharmony_ci ixp4xx_timer2_irq = irq; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci err = input_register_device(input_dev); 1308c2ecf20Sopenharmony_ci if (err) 1318c2ecf20Sopenharmony_ci goto err_free_irq; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci platform_set_drvdata(dev, input_dev); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err_free_irq: 1388c2ecf20Sopenharmony_ci free_irq(irq, (void *)dev->id); 1398c2ecf20Sopenharmony_ci err_free_gpio: 1408c2ecf20Sopenharmony_ci gpio_free(dev->id); 1418c2ecf20Sopenharmony_ci err_free_device: 1428c2ecf20Sopenharmony_ci input_free_device(input_dev); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int ixp4xx_spkr_remove(struct platform_device *dev) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct input_dev *input_dev = platform_get_drvdata(dev); 1508c2ecf20Sopenharmony_ci unsigned int pin = (unsigned int) input_get_drvdata(input_dev); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci input_unregister_device(input_dev); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* turn the speaker off */ 1558c2ecf20Sopenharmony_ci disable_irq(ixp4xx_timer2_irq); 1568c2ecf20Sopenharmony_ci ixp4xx_spkr_control(pin, 0); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci free_irq(ixp4xx_timer2_irq, (void *)dev->id); 1598c2ecf20Sopenharmony_ci gpio_free(dev->id); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void ixp4xx_spkr_shutdown(struct platform_device *dev) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct input_dev *input_dev = platform_get_drvdata(dev); 1678c2ecf20Sopenharmony_ci unsigned int pin = (unsigned int) input_get_drvdata(input_dev); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* turn off the speaker */ 1708c2ecf20Sopenharmony_ci disable_irq(ixp4xx_timer2_irq); 1718c2ecf20Sopenharmony_ci ixp4xx_spkr_control(pin, 0); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic struct platform_driver ixp4xx_spkr_platform_driver = { 1758c2ecf20Sopenharmony_ci .driver = { 1768c2ecf20Sopenharmony_ci .name = "ixp4xx-beeper", 1778c2ecf20Sopenharmony_ci }, 1788c2ecf20Sopenharmony_ci .probe = ixp4xx_spkr_probe, 1798c2ecf20Sopenharmony_ci .remove = ixp4xx_spkr_remove, 1808c2ecf20Sopenharmony_ci .shutdown = ixp4xx_spkr_shutdown, 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_cimodule_platform_driver(ixp4xx_spkr_platform_driver); 1838c2ecf20Sopenharmony_ci 184