11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Driver for PC-speaker like devices found on various Sparc systems. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2002 Vojtech Pavlik 59c1a5077SDavid S. Miller * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net) 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds #include <linux/kernel.h> 81da177e4SLinus Torvalds #include <linux/module.h> 91da177e4SLinus Torvalds #include <linux/init.h> 101da177e4SLinus Torvalds #include <linux/input.h> 119c1a5077SDavid S. Miller #include <linux/of_device.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <asm/io.h> 151da177e4SLinus Torvalds 16a2bd4fd1SDavid S. Miller MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); 1776b7cddfSDmitry Torokhov MODULE_DESCRIPTION("Sparc Speaker beeper driver"); 181da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 191da177e4SLinus Torvalds 209c1a5077SDavid S. Miller struct grover_beep_info { 219c1a5077SDavid S. Miller void __iomem *freq_regs; 229c1a5077SDavid S. Miller void __iomem *enable_reg; 239c1a5077SDavid S. Miller }; 249c1a5077SDavid S. Miller 259c1a5077SDavid S. Miller struct bbc_beep_info { 269c1a5077SDavid S. Miller u32 clock_freq; 279c1a5077SDavid S. Miller void __iomem *regs; 289c1a5077SDavid S. Miller }; 299c1a5077SDavid S. Miller 30a2bd4fd1SDavid S. Miller struct sparcspkr_state { 31a2bd4fd1SDavid S. Miller const char *name; 32a2bd4fd1SDavid S. Miller int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); 33a2bd4fd1SDavid S. Miller spinlock_t lock; 34a2bd4fd1SDavid S. Miller struct input_dev *input_dev; 359c1a5077SDavid S. Miller union { 369c1a5077SDavid S. Miller struct grover_beep_info grover; 379c1a5077SDavid S. Miller struct bbc_beep_info bbc; 389c1a5077SDavid S. Miller } u; 39a2bd4fd1SDavid S. Miller }; 401da177e4SLinus Torvalds 419c1a5077SDavid S. Miller static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count) 429c1a5077SDavid S. Miller { 439c1a5077SDavid S. Miller u32 val, clock_freq = info->clock_freq; 449c1a5077SDavid S. Miller int i; 459c1a5077SDavid S. Miller 469c1a5077SDavid S. Miller if (!count) 479c1a5077SDavid S. Miller return 0; 489c1a5077SDavid S. Miller 499c1a5077SDavid S. Miller if (count <= clock_freq >> 20) 509c1a5077SDavid S. Miller return 1 << 18; 519c1a5077SDavid S. Miller 529c1a5077SDavid S. Miller if (count >= clock_freq >> 12) 539c1a5077SDavid S. Miller return 1 << 10; 549c1a5077SDavid S. Miller 559c1a5077SDavid S. Miller val = 1 << 18; 569c1a5077SDavid S. Miller for (i = 19; i >= 11; i--) { 579c1a5077SDavid S. Miller val >>= 1; 589c1a5077SDavid S. Miller if (count <= clock_freq >> i) 599c1a5077SDavid S. Miller break; 609c1a5077SDavid S. Miller } 619c1a5077SDavid S. Miller 629c1a5077SDavid S. Miller return val; 639c1a5077SDavid S. Miller } 649c1a5077SDavid S. Miller 659c1a5077SDavid S. Miller static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 661da177e4SLinus Torvalds { 67293e6392SDmitry Torokhov struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); 689c1a5077SDavid S. Miller struct bbc_beep_info *info = &state->u.bbc; 691da177e4SLinus Torvalds unsigned int count = 0; 701da177e4SLinus Torvalds unsigned long flags; 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds if (type != EV_SND) 731da177e4SLinus Torvalds return -1; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds switch (code) { 761da177e4SLinus Torvalds case SND_BELL: if (value) value = 1000; 771da177e4SLinus Torvalds case SND_TONE: break; 781da177e4SLinus Torvalds default: return -1; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds if (value > 20 && value < 32767) 821da177e4SLinus Torvalds count = 1193182 / value; 831da177e4SLinus Torvalds 849c1a5077SDavid S. Miller count = bbc_count_to_reg(info, count); 859c1a5077SDavid S. Miller 86a2bd4fd1SDavid S. Miller spin_lock_irqsave(&state->lock, flags); 871da177e4SLinus Torvalds 889c1a5077SDavid S. Miller if (count) { 89d650471aSSam Ravnborg sbus_writeb(0x01, info->regs + 0); 90d650471aSSam Ravnborg sbus_writeb(0x00, info->regs + 2); 91d650471aSSam Ravnborg sbus_writeb((count >> 16) & 0xff, info->regs + 3); 92d650471aSSam Ravnborg sbus_writeb((count >> 8) & 0xff, info->regs + 4); 93d650471aSSam Ravnborg sbus_writeb(0x00, info->regs + 5); 949c1a5077SDavid S. Miller } else { 95d650471aSSam Ravnborg sbus_writeb(0x00, info->regs + 0); 969c1a5077SDavid S. Miller } 971da177e4SLinus Torvalds 98a2bd4fd1SDavid S. Miller spin_unlock_irqrestore(&state->lock, flags); 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds return 0; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1039c1a5077SDavid S. Miller static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 1041da177e4SLinus Torvalds { 105293e6392SDmitry Torokhov struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); 1069c1a5077SDavid S. Miller struct grover_beep_info *info = &state->u.grover; 1071da177e4SLinus Torvalds unsigned int count = 0; 1081da177e4SLinus Torvalds unsigned long flags; 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds if (type != EV_SND) 1111da177e4SLinus Torvalds return -1; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds switch (code) { 1141da177e4SLinus Torvalds case SND_BELL: if (value) value = 1000; 1151da177e4SLinus Torvalds case SND_TONE: break; 1161da177e4SLinus Torvalds default: return -1; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds if (value > 20 && value < 32767) 1201da177e4SLinus Torvalds count = 1193182 / value; 1211da177e4SLinus Torvalds 122a2bd4fd1SDavid S. Miller spin_lock_irqsave(&state->lock, flags); 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds if (count) { 1251da177e4SLinus Torvalds /* enable counter 2 */ 126d650471aSSam Ravnborg sbus_writeb(sbus_readb(info->enable_reg) | 3, info->enable_reg); 1271da177e4SLinus Torvalds /* set command for counter 2, 2 byte write */ 128d650471aSSam Ravnborg sbus_writeb(0xB6, info->freq_regs + 1); 1291da177e4SLinus Torvalds /* select desired HZ */ 130d650471aSSam Ravnborg sbus_writeb(count & 0xff, info->freq_regs + 0); 131d650471aSSam Ravnborg sbus_writeb((count >> 8) & 0xff, info->freq_regs + 0); 1321da177e4SLinus Torvalds } else { 1331da177e4SLinus Torvalds /* disable counter 2 */ 134d650471aSSam Ravnborg sbus_writeb(sbus_readb(info->enable_reg) & 0xFC, info->enable_reg); 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds 137a2bd4fd1SDavid S. Miller spin_unlock_irqrestore(&state->lock, flags); 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds return 0; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds 1425298cc4cSBill Pemberton static int sparcspkr_probe(struct device *dev) 1431da177e4SLinus Torvalds { 144a2bd4fd1SDavid S. Miller struct sparcspkr_state *state = dev_get_drvdata(dev); 145f5b64078SDmitry Torokhov struct input_dev *input_dev; 146f5b64078SDmitry Torokhov int error; 1471da177e4SLinus Torvalds 148f5b64078SDmitry Torokhov input_dev = input_allocate_device(); 149f5b64078SDmitry Torokhov if (!input_dev) 15076b7cddfSDmitry Torokhov return -ENOMEM; 15176b7cddfSDmitry Torokhov 152a2bd4fd1SDavid S. Miller input_dev->name = state->name; 153f5b64078SDmitry Torokhov input_dev->phys = "sparc/input0"; 154f5b64078SDmitry Torokhov input_dev->id.bustype = BUS_ISA; 155f5b64078SDmitry Torokhov input_dev->id.vendor = 0x001f; 156f5b64078SDmitry Torokhov input_dev->id.product = 0x0001; 157f5b64078SDmitry Torokhov input_dev->id.version = 0x0100; 158293e6392SDmitry Torokhov input_dev->dev.parent = dev; 1591da177e4SLinus Torvalds 1607b19ada2SJiri Slaby input_dev->evbit[0] = BIT_MASK(EV_SND); 1617b19ada2SJiri Slaby input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); 1621da177e4SLinus Torvalds 163a2bd4fd1SDavid S. Miller input_dev->event = state->event; 164f5b64078SDmitry Torokhov 165f5b64078SDmitry Torokhov error = input_register_device(input_dev); 166f5b64078SDmitry Torokhov if (error) { 167f5b64078SDmitry Torokhov input_free_device(input_dev); 168f5b64078SDmitry Torokhov return error; 169f5b64078SDmitry Torokhov } 170f5b64078SDmitry Torokhov 171a2bd4fd1SDavid S. Miller state->input_dev = input_dev; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds return 0; 1741da177e4SLinus Torvalds } 175f5b64078SDmitry Torokhov 1764ebb24f7SGrant Likely static void sparcspkr_shutdown(struct platform_device *dev) 177f5b64078SDmitry Torokhov { 17835c4b122SJingoo Han struct sparcspkr_state *state = platform_get_drvdata(dev); 179a2bd4fd1SDavid S. Miller struct input_dev *input_dev = state->input_dev; 180a2bd4fd1SDavid S. Miller 181f5b64078SDmitry Torokhov /* turn off the speaker */ 182a2bd4fd1SDavid S. Miller state->event(input_dev, EV_SND, SND_BELL, 0); 183f5b64078SDmitry Torokhov } 184f5b64078SDmitry Torokhov 1855298cc4cSBill Pemberton static int bbc_beep_probe(struct platform_device *op) 186a2bd4fd1SDavid S. Miller { 187a2bd4fd1SDavid S. Miller struct sparcspkr_state *state; 1889c1a5077SDavid S. Miller struct bbc_beep_info *info; 1899c1a5077SDavid S. Miller struct device_node *dp; 1909c1a5077SDavid S. Miller int err = -ENOMEM; 191a2bd4fd1SDavid S. Miller 192a2bd4fd1SDavid S. Miller state = kzalloc(sizeof(*state), GFP_KERNEL); 193a2bd4fd1SDavid S. Miller if (!state) 1949c1a5077SDavid S. Miller goto out_err; 195a2bd4fd1SDavid S. Miller 1969c1a5077SDavid S. Miller state->name = "Sparc BBC Speaker"; 1979c1a5077SDavid S. Miller state->event = bbc_spkr_event; 198a2bd4fd1SDavid S. Miller spin_lock_init(&state->lock); 199a2bd4fd1SDavid S. Miller 2009c1a5077SDavid S. Miller dp = of_find_node_by_path("/"); 2019c1a5077SDavid S. Miller err = -ENODEV; 2029c1a5077SDavid S. Miller if (!dp) 2039c1a5077SDavid S. Miller goto out_free; 204a2bd4fd1SDavid S. Miller 2059c1a5077SDavid S. Miller info = &state->u.bbc; 2069c1a5077SDavid S. Miller info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0); 2079c1a5077SDavid S. Miller if (!info->clock_freq) 2089c1a5077SDavid S. Miller goto out_free; 2099c1a5077SDavid S. Miller 2109c1a5077SDavid S. Miller info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep"); 2119c1a5077SDavid S. Miller if (!info->regs) 2129c1a5077SDavid S. Miller goto out_free; 2139c1a5077SDavid S. Miller 21435c4b122SJingoo Han platform_set_drvdata(op, state); 2159c1a5077SDavid S. Miller 2169c1a5077SDavid S. Miller err = sparcspkr_probe(&op->dev); 2179c1a5077SDavid S. Miller if (err) 2189c1a5077SDavid S. Miller goto out_clear_drvdata; 2199c1a5077SDavid S. Miller 2209c1a5077SDavid S. Miller return 0; 2219c1a5077SDavid S. Miller 2229c1a5077SDavid S. Miller out_clear_drvdata: 2239c1a5077SDavid S. Miller of_iounmap(&op->resource[0], info->regs, 6); 2249c1a5077SDavid S. Miller 2259c1a5077SDavid S. Miller out_free: 226a2bd4fd1SDavid S. Miller kfree(state); 2279c1a5077SDavid S. Miller out_err: 2289c1a5077SDavid S. Miller return err; 229a2bd4fd1SDavid S. Miller } 230a2bd4fd1SDavid S. Miller 231e2619cf7SBill Pemberton static int bbc_remove(struct platform_device *op) 2329c1a5077SDavid S. Miller { 23335c4b122SJingoo Han struct sparcspkr_state *state = platform_get_drvdata(op); 2349c1a5077SDavid S. Miller struct input_dev *input_dev = state->input_dev; 2359c1a5077SDavid S. Miller struct bbc_beep_info *info = &state->u.bbc; 2369c1a5077SDavid S. Miller 2379c1a5077SDavid S. Miller /* turn off the speaker */ 2389c1a5077SDavid S. Miller state->event(input_dev, EV_SND, SND_BELL, 0); 2399c1a5077SDavid S. Miller 2409c1a5077SDavid S. Miller input_unregister_device(input_dev); 2419c1a5077SDavid S. Miller 2429c1a5077SDavid S. Miller of_iounmap(&op->resource[0], info->regs, 6); 2439c1a5077SDavid S. Miller 2449c1a5077SDavid S. Miller kfree(state); 2459c1a5077SDavid S. Miller 246a2bd4fd1SDavid S. Miller return 0; 247a2bd4fd1SDavid S. Miller } 248a2bd4fd1SDavid S. Miller 249fd098316SDavid S. Miller static const struct of_device_id bbc_beep_match[] = { 250a2bd4fd1SDavid S. Miller { 251a2bd4fd1SDavid S. Miller .name = "beep", 2529c1a5077SDavid S. Miller .compatible = "SUNW,bbc-beep", 253f5b64078SDmitry Torokhov }, 254a2bd4fd1SDavid S. Miller {}, 255a2bd4fd1SDavid S. Miller }; 256a2bd4fd1SDavid S. Miller 2574ebb24f7SGrant Likely static struct platform_driver bbc_beep_driver = { 2584018294bSGrant Likely .driver = { 2599c1a5077SDavid S. Miller .name = "bbcbeep", 2604018294bSGrant Likely .owner = THIS_MODULE, 2614018294bSGrant Likely .of_match_table = bbc_beep_match, 2624018294bSGrant Likely }, 2639c1a5077SDavid S. Miller .probe = bbc_beep_probe, 2641cb0aa88SBill Pemberton .remove = bbc_remove, 265f5b64078SDmitry Torokhov .shutdown = sparcspkr_shutdown, 266f5b64078SDmitry Torokhov }; 267f5b64078SDmitry Torokhov 2685298cc4cSBill Pemberton static int grover_beep_probe(struct platform_device *op) 269f5b64078SDmitry Torokhov { 270a2bd4fd1SDavid S. Miller struct sparcspkr_state *state; 2719c1a5077SDavid S. Miller struct grover_beep_info *info; 2729c1a5077SDavid S. Miller int err = -ENOMEM; 273f5b64078SDmitry Torokhov 274a2bd4fd1SDavid S. Miller state = kzalloc(sizeof(*state), GFP_KERNEL); 275a2bd4fd1SDavid S. Miller if (!state) 2769c1a5077SDavid S. Miller goto out_err; 277f5b64078SDmitry Torokhov 2789c1a5077SDavid S. Miller state->name = "Sparc Grover Speaker"; 2799c1a5077SDavid S. Miller state->event = grover_spkr_event; 280a2bd4fd1SDavid S. Miller spin_lock_init(&state->lock); 281a2bd4fd1SDavid S. Miller 2829c1a5077SDavid S. Miller info = &state->u.grover; 2839c1a5077SDavid S. Miller info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq"); 2849c1a5077SDavid S. Miller if (!info->freq_regs) 2859c1a5077SDavid S. Miller goto out_free; 286a2bd4fd1SDavid S. Miller 2879c1a5077SDavid S. Miller info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable"); 2889c1a5077SDavid S. Miller if (!info->enable_reg) 2899c1a5077SDavid S. Miller goto out_unmap_freq_regs; 2909c1a5077SDavid S. Miller 29135c4b122SJingoo Han platform_set_drvdata(op, state); 2929c1a5077SDavid S. Miller 2939c1a5077SDavid S. Miller err = sparcspkr_probe(&op->dev); 2949c1a5077SDavid S. Miller if (err) 2959c1a5077SDavid S. Miller goto out_clear_drvdata; 2969c1a5077SDavid S. Miller 2979c1a5077SDavid S. Miller return 0; 2989c1a5077SDavid S. Miller 2999c1a5077SDavid S. Miller out_clear_drvdata: 3009c1a5077SDavid S. Miller of_iounmap(&op->resource[3], info->enable_reg, 1); 3019c1a5077SDavid S. Miller 3029c1a5077SDavid S. Miller out_unmap_freq_regs: 3039c1a5077SDavid S. Miller of_iounmap(&op->resource[2], info->freq_regs, 2); 3049c1a5077SDavid S. Miller out_free: 305a2bd4fd1SDavid S. Miller kfree(state); 3069c1a5077SDavid S. Miller out_err: 3079c1a5077SDavid S. Miller return err; 308f5b64078SDmitry Torokhov } 309f5b64078SDmitry Torokhov 310e2619cf7SBill Pemberton static int grover_remove(struct platform_device *op) 3119c1a5077SDavid S. Miller { 31235c4b122SJingoo Han struct sparcspkr_state *state = platform_get_drvdata(op); 3139c1a5077SDavid S. Miller struct grover_beep_info *info = &state->u.grover; 3149c1a5077SDavid S. Miller struct input_dev *input_dev = state->input_dev; 3159c1a5077SDavid S. Miller 3169c1a5077SDavid S. Miller /* turn off the speaker */ 3179c1a5077SDavid S. Miller state->event(input_dev, EV_SND, SND_BELL, 0); 3189c1a5077SDavid S. Miller 3199c1a5077SDavid S. Miller input_unregister_device(input_dev); 3209c1a5077SDavid S. Miller 3219c1a5077SDavid S. Miller of_iounmap(&op->resource[3], info->enable_reg, 1); 3229c1a5077SDavid S. Miller of_iounmap(&op->resource[2], info->freq_regs, 2); 3239c1a5077SDavid S. Miller 3249c1a5077SDavid S. Miller kfree(state); 3259c1a5077SDavid S. Miller 326f5b64078SDmitry Torokhov return 0; 327f5b64078SDmitry Torokhov } 3281da177e4SLinus Torvalds 329fd098316SDavid S. Miller static const struct of_device_id grover_beep_match[] = { 330a2bd4fd1SDavid S. Miller { 3319c1a5077SDavid S. Miller .name = "beep", 3329c1a5077SDavid S. Miller .compatible = "SUNW,smbus-beep", 333a2bd4fd1SDavid S. Miller }, 334a2bd4fd1SDavid S. Miller {}, 335a2bd4fd1SDavid S. Miller }; 336a2bd4fd1SDavid S. Miller 3374ebb24f7SGrant Likely static struct platform_driver grover_beep_driver = { 3384018294bSGrant Likely .driver = { 3399c1a5077SDavid S. Miller .name = "groverbeep", 3404018294bSGrant Likely .owner = THIS_MODULE, 3414018294bSGrant Likely .of_match_table = grover_beep_match, 3424018294bSGrant Likely }, 3439c1a5077SDavid S. Miller .probe = grover_beep_probe, 3441cb0aa88SBill Pemberton .remove = grover_remove, 345a2bd4fd1SDavid S. Miller .shutdown = sparcspkr_shutdown, 346a2bd4fd1SDavid S. Miller }; 347a2bd4fd1SDavid S. Miller 3481da177e4SLinus Torvalds static int __init sparcspkr_init(void) 3491da177e4SLinus Torvalds { 3504ebb24f7SGrant Likely int err = platform_driver_register(&bbc_beep_driver); 3511da177e4SLinus Torvalds 352a2bd4fd1SDavid S. Miller if (!err) { 3534ebb24f7SGrant Likely err = platform_driver_register(&grover_beep_driver); 354a2bd4fd1SDavid S. Miller if (err) 3554ebb24f7SGrant Likely platform_driver_unregister(&bbc_beep_driver); 356f5b64078SDmitry Torokhov } 3571da177e4SLinus Torvalds 358a2bd4fd1SDavid S. Miller return err; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds static void __exit sparcspkr_exit(void) 3621da177e4SLinus Torvalds { 3634ebb24f7SGrant Likely platform_driver_unregister(&bbc_beep_driver); 3644ebb24f7SGrant Likely platform_driver_unregister(&grover_beep_driver); 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds module_init(sparcspkr_init); 3681da177e4SLinus Torvalds module_exit(sparcspkr_exit); 369