162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (C) 2014 Broadcom Corporation 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bitops.h> 562306a36Sopenharmony_ci#include <linux/clk.h> 662306a36Sopenharmony_ci#include <linux/gfp.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/input.h> 962306a36Sopenharmony_ci#include <linux/input/matrix_keypad.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/stddef.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define DEFAULT_CLK_HZ 31250 1862306a36Sopenharmony_ci#define MAX_ROWS 8 1962306a36Sopenharmony_ci#define MAX_COLS 8 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* Register/field definitions */ 2262306a36Sopenharmony_ci#define KPCR_OFFSET 0x00000080 2362306a36Sopenharmony_ci#define KPCR_MODE 0x00000002 2462306a36Sopenharmony_ci#define KPCR_MODE_SHIFT 1 2562306a36Sopenharmony_ci#define KPCR_MODE_MASK 1 2662306a36Sopenharmony_ci#define KPCR_ENABLE 0x00000001 2762306a36Sopenharmony_ci#define KPCR_STATUSFILTERENABLE 0x00008000 2862306a36Sopenharmony_ci#define KPCR_STATUSFILTERTYPE_SHIFT 12 2962306a36Sopenharmony_ci#define KPCR_COLFILTERENABLE 0x00000800 3062306a36Sopenharmony_ci#define KPCR_COLFILTERTYPE_SHIFT 8 3162306a36Sopenharmony_ci#define KPCR_ROWWIDTH_SHIFT 20 3262306a36Sopenharmony_ci#define KPCR_COLUMNWIDTH_SHIFT 16 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define KPIOR_OFFSET 0x00000084 3562306a36Sopenharmony_ci#define KPIOR_ROWOCONTRL_SHIFT 24 3662306a36Sopenharmony_ci#define KPIOR_ROWOCONTRL_MASK 0xFF000000 3762306a36Sopenharmony_ci#define KPIOR_COLUMNOCONTRL_SHIFT 16 3862306a36Sopenharmony_ci#define KPIOR_COLUMNOCONTRL_MASK 0x00FF0000 3962306a36Sopenharmony_ci#define KPIOR_COLUMN_IO_DATA_SHIFT 0 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define KPEMR0_OFFSET 0x00000090 4262306a36Sopenharmony_ci#define KPEMR1_OFFSET 0x00000094 4362306a36Sopenharmony_ci#define KPEMR2_OFFSET 0x00000098 4462306a36Sopenharmony_ci#define KPEMR3_OFFSET 0x0000009C 4562306a36Sopenharmony_ci#define KPEMR_EDGETYPE_BOTH 3 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define KPSSR0_OFFSET 0x000000A0 4862306a36Sopenharmony_ci#define KPSSR1_OFFSET 0x000000A4 4962306a36Sopenharmony_ci#define KPSSRN_OFFSET(reg_n) (KPSSR0_OFFSET + 4 * (reg_n)) 5062306a36Sopenharmony_ci#define KPIMR0_OFFSET 0x000000B0 5162306a36Sopenharmony_ci#define KPIMR1_OFFSET 0x000000B4 5262306a36Sopenharmony_ci#define KPICR0_OFFSET 0x000000B8 5362306a36Sopenharmony_ci#define KPICR1_OFFSET 0x000000BC 5462306a36Sopenharmony_ci#define KPICRN_OFFSET(reg_n) (KPICR0_OFFSET + 4 * (reg_n)) 5562306a36Sopenharmony_ci#define KPISR0_OFFSET 0x000000C0 5662306a36Sopenharmony_ci#define KPISR1_OFFSET 0x000000C4 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define KPCR_STATUSFILTERTYPE_MAX 7 5962306a36Sopenharmony_ci#define KPCR_COLFILTERTYPE_MAX 7 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Macros to determine the row/column from a bit that is set in SSR0/1. */ 6262306a36Sopenharmony_ci#define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n)) 6362306a36Sopenharmony_ci#define BIT_TO_COL(bit_nr) ((bit_nr) % 8) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Structure representing various run-time entities */ 6662306a36Sopenharmony_cistruct bcm_kp { 6762306a36Sopenharmony_ci void __iomem *base; 6862306a36Sopenharmony_ci int irq; 6962306a36Sopenharmony_ci struct clk *clk; 7062306a36Sopenharmony_ci struct input_dev *input_dev; 7162306a36Sopenharmony_ci unsigned long last_state[2]; 7262306a36Sopenharmony_ci unsigned int n_rows; 7362306a36Sopenharmony_ci unsigned int n_cols; 7462306a36Sopenharmony_ci u32 kpcr; 7562306a36Sopenharmony_ci u32 kpior; 7662306a36Sopenharmony_ci u32 kpemr; 7762306a36Sopenharmony_ci u32 imr0_val; 7862306a36Sopenharmony_ci u32 imr1_val; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * Returns the keycode from the input device keymap given the row and 8362306a36Sopenharmony_ci * column. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned int row_shift = get_count_order(kp->n_cols); 8862306a36Sopenharmony_ci unsigned short *keymap = kp->input_dev->keycode; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long state, change; 9662306a36Sopenharmony_ci int bit_nr; 9762306a36Sopenharmony_ci int key_press; 9862306a36Sopenharmony_ci int row, col; 9962306a36Sopenharmony_ci unsigned int keycode; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Clear interrupts */ 10262306a36Sopenharmony_ci writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num)); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci state = readl(kp->base + KPSSRN_OFFSET(reg_num)); 10562306a36Sopenharmony_ci change = kp->last_state[reg_num] ^ state; 10662306a36Sopenharmony_ci kp->last_state[reg_num] = state; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { 10962306a36Sopenharmony_ci key_press = state & BIT(bit_nr); 11062306a36Sopenharmony_ci /* The meaning of SSR register depends on pull mode. */ 11162306a36Sopenharmony_ci key_press = pull_mode ? !key_press : key_press; 11262306a36Sopenharmony_ci row = BIT_TO_ROW_SSRN(bit_nr, reg_num); 11362306a36Sopenharmony_ci col = BIT_TO_COL(bit_nr); 11462306a36Sopenharmony_ci keycode = bcm_kp_get_keycode(kp, row, col); 11562306a36Sopenharmony_ci input_report_key(kp->input_dev, keycode, key_press); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct bcm_kp *kp = dev_id; 12262306a36Sopenharmony_ci int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK; 12362306a36Sopenharmony_ci int reg_num; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci for (reg_num = 0; reg_num <= 1; reg_num++) 12662306a36Sopenharmony_ci bcm_kp_report_keys(kp, reg_num, pull_mode); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci input_sync(kp->input_dev); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return IRQ_HANDLED; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int bcm_kp_start(struct bcm_kp *kp) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci int error; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (kp->clk) { 13862306a36Sopenharmony_ci error = clk_prepare_enable(kp->clk); 13962306a36Sopenharmony_ci if (error) 14062306a36Sopenharmony_ci return error; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci writel(kp->kpior, kp->base + KPIOR_OFFSET); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci writel(kp->imr0_val, kp->base + KPIMR0_OFFSET); 14662306a36Sopenharmony_ci writel(kp->imr1_val, kp->base + KPIMR1_OFFSET); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci writel(kp->kpemr, kp->base + KPEMR0_OFFSET); 14962306a36Sopenharmony_ci writel(kp->kpemr, kp->base + KPEMR1_OFFSET); 15062306a36Sopenharmony_ci writel(kp->kpemr, kp->base + KPEMR2_OFFSET); 15162306a36Sopenharmony_ci writel(kp->kpemr, kp->base + KPEMR3_OFFSET); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); 15462306a36Sopenharmony_ci writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET); 15762306a36Sopenharmony_ci kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void bcm_kp_stop(const struct bcm_kp *kp) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci u32 val; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci val = readl(kp->base + KPCR_OFFSET); 16962306a36Sopenharmony_ci val &= ~KPCR_ENABLE; 17062306a36Sopenharmony_ci writel(0, kp->base + KPCR_OFFSET); 17162306a36Sopenharmony_ci writel(0, kp->base + KPIMR0_OFFSET); 17262306a36Sopenharmony_ci writel(0, kp->base + KPIMR1_OFFSET); 17362306a36Sopenharmony_ci writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); 17462306a36Sopenharmony_ci writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci clk_disable_unprepare(kp->clk); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int bcm_kp_open(struct input_dev *dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct bcm_kp *kp = input_get_drvdata(dev); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return bcm_kp_start(kp); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void bcm_kp_close(struct input_dev *dev) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct bcm_kp *kp = input_get_drvdata(dev); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci bcm_kp_stop(kp); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct device *dev = kp->input_dev->dev.parent; 19662306a36Sopenharmony_ci struct device_node *np = dev->of_node; 19762306a36Sopenharmony_ci int error; 19862306a36Sopenharmony_ci unsigned int dt_val; 19962306a36Sopenharmony_ci unsigned int i; 20062306a36Sopenharmony_ci unsigned int num_rows, col_mask, rows_set; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Initialize the KPCR Keypad Configuration Register */ 20362306a36Sopenharmony_ci kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci error = matrix_keypad_parse_properties(dev, &kp->n_rows, &kp->n_cols); 20662306a36Sopenharmony_ci if (error) { 20762306a36Sopenharmony_ci dev_err(dev, "failed to parse kp params\n"); 20862306a36Sopenharmony_ci return error; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Set row width for the ASIC block. */ 21262306a36Sopenharmony_ci kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Set column width for the ASIC block. */ 21562306a36Sopenharmony_ci kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Configure the IMR registers */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* 22062306a36Sopenharmony_ci * IMR registers contain interrupt enable bits for 8x8 matrix 22162306a36Sopenharmony_ci * IMR0 register format: <row3> <row2> <row1> <row0> 22262306a36Sopenharmony_ci * IMR1 register format: <row7> <row6> <row5> <row4> 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci col_mask = (1 << (kp->n_cols)) - 1; 22562306a36Sopenharmony_ci num_rows = kp->n_rows; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Set column bits in rows 0 to 3 in IMR0 */ 22862306a36Sopenharmony_ci kp->imr0_val = col_mask; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci rows_set = 1; 23162306a36Sopenharmony_ci while (--num_rows && rows_set++ < 4) 23262306a36Sopenharmony_ci kp->imr0_val |= kp->imr0_val << MAX_COLS; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Set column bits in rows 4 to 7 in IMR1 */ 23562306a36Sopenharmony_ci kp->imr1_val = 0; 23662306a36Sopenharmony_ci if (num_rows) { 23762306a36Sopenharmony_ci kp->imr1_val = col_mask; 23862306a36Sopenharmony_ci while (--num_rows) 23962306a36Sopenharmony_ci kp->imr1_val |= kp->imr1_val << MAX_COLS; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Initialize the KPEMR Keypress Edge Mode Registers */ 24362306a36Sopenharmony_ci /* Trigger on both edges */ 24462306a36Sopenharmony_ci kp->kpemr = 0; 24562306a36Sopenharmony_ci for (i = 0; i <= 30; i += 2) 24662306a36Sopenharmony_ci kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Obtain the Status filter debounce value and verify against the 25062306a36Sopenharmony_ci * possible values specified in the DT binding. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci of_property_read_u32(np, "status-debounce-filter-period", &dt_val); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (dt_val > KPCR_STATUSFILTERTYPE_MAX) { 25562306a36Sopenharmony_ci dev_err(dev, "Invalid Status filter debounce value %d\n", 25662306a36Sopenharmony_ci dt_val); 25762306a36Sopenharmony_ci return -EINVAL; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* 26362306a36Sopenharmony_ci * Obtain the Column filter debounce value and verify against the 26462306a36Sopenharmony_ci * possible values specified in the DT binding. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci of_property_read_u32(np, "col-debounce-filter-period", &dt_val); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (dt_val > KPCR_COLFILTERTYPE_MAX) { 26962306a36Sopenharmony_ci dev_err(dev, "Invalid Column filter debounce value %d\n", 27062306a36Sopenharmony_ci dt_val); 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * Determine between the row and column, 27862306a36Sopenharmony_ci * which should be configured as output. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci if (of_property_read_bool(np, "row-output-enabled")) { 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * Set RowOContrl or ColumnOContrl in KPIOR 28362306a36Sopenharmony_ci * to the number of pins to drive as outputs 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci kp->kpior = ((1 << kp->n_rows) - 1) << 28662306a36Sopenharmony_ci KPIOR_ROWOCONTRL_SHIFT; 28762306a36Sopenharmony_ci } else { 28862306a36Sopenharmony_ci kp->kpior = ((1 << kp->n_cols) - 1) << 28962306a36Sopenharmony_ci KPIOR_COLUMNOCONTRL_SHIFT; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* 29362306a36Sopenharmony_ci * Determine if the scan pull up needs to be enabled 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (of_property_read_bool(np, "pull-up-enabled")) 29662306a36Sopenharmony_ci kp->kpcr |= KPCR_MODE; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n", 29962306a36Sopenharmony_ci kp->n_rows, kp->n_cols, 30062306a36Sopenharmony_ci kp->kpcr, kp->kpior, kp->kpemr); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int bcm_kp_probe(struct platform_device *pdev) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct bcm_kp *kp; 30962306a36Sopenharmony_ci struct input_dev *input_dev; 31062306a36Sopenharmony_ci int error; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); 31362306a36Sopenharmony_ci if (!kp) 31462306a36Sopenharmony_ci return -ENOMEM; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci input_dev = devm_input_allocate_device(&pdev->dev); 31762306a36Sopenharmony_ci if (!input_dev) { 31862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate the input device\n"); 31962306a36Sopenharmony_ci return -ENOMEM; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci __set_bit(EV_KEY, input_dev->evbit); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Enable auto repeat feature of Linux input subsystem */ 32562306a36Sopenharmony_ci if (of_property_read_bool(pdev->dev.of_node, "autorepeat")) 32662306a36Sopenharmony_ci __set_bit(EV_REP, input_dev->evbit); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci input_dev->name = pdev->name; 32962306a36Sopenharmony_ci input_dev->phys = "keypad/input0"; 33062306a36Sopenharmony_ci input_dev->dev.parent = &pdev->dev; 33162306a36Sopenharmony_ci input_dev->open = bcm_kp_open; 33262306a36Sopenharmony_ci input_dev->close = bcm_kp_close; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci input_dev->id.bustype = BUS_HOST; 33562306a36Sopenharmony_ci input_dev->id.vendor = 0x0001; 33662306a36Sopenharmony_ci input_dev->id.product = 0x0001; 33762306a36Sopenharmony_ci input_dev->id.version = 0x0100; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci input_set_drvdata(input_dev, kp); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci kp->input_dev = input_dev; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci error = bcm_kp_matrix_key_parse_dt(kp); 34462306a36Sopenharmony_ci if (error) 34562306a36Sopenharmony_ci return error; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci error = matrix_keypad_build_keymap(NULL, NULL, 34862306a36Sopenharmony_ci kp->n_rows, kp->n_cols, 34962306a36Sopenharmony_ci NULL, input_dev); 35062306a36Sopenharmony_ci if (error) { 35162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to build keymap\n"); 35262306a36Sopenharmony_ci return error; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci kp->base = devm_platform_ioremap_resource(pdev, 0); 35662306a36Sopenharmony_ci if (IS_ERR(kp->base)) 35762306a36Sopenharmony_ci return PTR_ERR(kp->base); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* Enable clock */ 36062306a36Sopenharmony_ci kp->clk = devm_clk_get_optional(&pdev->dev, "peri_clk"); 36162306a36Sopenharmony_ci if (IS_ERR(kp->clk)) { 36262306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(kp->clk), "Failed to get clock\n"); 36362306a36Sopenharmony_ci } else if (!kp->clk) { 36462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "No clock specified. Assuming it's enabled\n"); 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci unsigned int desired_rate; 36762306a36Sopenharmony_ci long actual_rate; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci error = of_property_read_u32(pdev->dev.of_node, 37062306a36Sopenharmony_ci "clock-frequency", &desired_rate); 37162306a36Sopenharmony_ci if (error < 0) 37262306a36Sopenharmony_ci desired_rate = DEFAULT_CLK_HZ; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci actual_rate = clk_round_rate(kp->clk, desired_rate); 37562306a36Sopenharmony_ci if (actual_rate <= 0) 37662306a36Sopenharmony_ci return -EINVAL; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci error = clk_set_rate(kp->clk, actual_rate); 37962306a36Sopenharmony_ci if (error) 38062306a36Sopenharmony_ci return error; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci error = clk_prepare_enable(kp->clk); 38362306a36Sopenharmony_ci if (error) 38462306a36Sopenharmony_ci return error; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Put the kp into a known sane state */ 38862306a36Sopenharmony_ci bcm_kp_stop(kp); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci kp->irq = platform_get_irq(pdev, 0); 39162306a36Sopenharmony_ci if (kp->irq < 0) 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci error = devm_request_threaded_irq(&pdev->dev, kp->irq, 39562306a36Sopenharmony_ci NULL, bcm_kp_isr_thread, 39662306a36Sopenharmony_ci IRQF_ONESHOT, pdev->name, kp); 39762306a36Sopenharmony_ci if (error) { 39862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request IRQ\n"); 39962306a36Sopenharmony_ci return error; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci error = input_register_device(input_dev); 40362306a36Sopenharmony_ci if (error) { 40462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register input device\n"); 40562306a36Sopenharmony_ci return error; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic const struct of_device_id bcm_kp_of_match[] = { 41262306a36Sopenharmony_ci { .compatible = "brcm,bcm-keypad" }, 41362306a36Sopenharmony_ci { }, 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_kp_of_match); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic struct platform_driver bcm_kp_device_driver = { 41862306a36Sopenharmony_ci .probe = bcm_kp_probe, 41962306a36Sopenharmony_ci .driver = { 42062306a36Sopenharmony_ci .name = "bcm-keypad", 42162306a36Sopenharmony_ci .of_match_table = of_match_ptr(bcm_kp_of_match), 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cimodule_platform_driver(bcm_kp_device_driver); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom Corporation"); 42862306a36Sopenharmony_ciMODULE_DESCRIPTION("BCM Keypad Driver"); 42962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 430